Merge JDK-8200758-branch
authorherrick
Wed, 27 Feb 2019 20:45:51 -0500
branchJDK-8200758-branch
changeset 57220 6a80fd1912ef
parent 57219 bfa094e6ce15 (diff)
parent 53947 8e069f7b4fab (current diff)
child 57241 e11f3bf34083
Merge
src/java.base/macosx/native/libjli/java_md_macosx.m
src/java.smartcardio/unix/native/libj2pcsc/MUSCLE/COPYING
--- a/make/CompileDemos.gmk	Wed Feb 27 12:19:29 2019 -0800
+++ b/make/CompileDemos.gmk	Wed Feb 27 20:45:51 2019 -0500
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2011, 2019, Oracle and/or its affiliates. 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
@@ -219,6 +219,11 @@
     MAIN_CLASS := transparentruler.Ruler, \
 ))
 
+$(eval $(call SetupBuildDemo, JNLPConverter, \
+    DEMO_SUBDIR := jpackage, \
+    MAIN_CLASS := jnlp.converter.Main, \
+))
+
 ################################################################################
 # Copy html and README files.
 
--- a/make/CompileJavaModules.gmk	Wed Feb 27 12:19:29 2019 -0800
+++ b/make/CompileJavaModules.gmk	Wed Feb 27 20:45:51 2019 -0500
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2014, 2019, Oracle and/or its affiliates. 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
@@ -380,6 +380,15 @@
 
 ################################################################################
 
+jdk.jpackage_ADD_JAVAC_FLAGS += -parameters -XDstringConcat=inline
+
+jdk.jpackage_COPY += .gif .png .txt .spec .script .prerm .preinst .postrm .postinst .list \
+    .desktop .copyright .control .plist .template .icns .scpt .entitlements .wxs .iss .ico .bmp
+
+jdk.jpackage_CLEAN += .properties
+
+################################################################################
+
 jdk.jconsole_COPY += .gif .png
 
 jdk.jconsole_CLEAN_FILES += $(wildcard \
--- a/make/common/Modules.gmk	Wed Feb 27 12:19:29 2019 -0800
+++ b/make/common/Modules.gmk	Wed Feb 27 20:45:51 2019 -0500
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2014, 2019, Oracle and/or its affiliates. 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
@@ -128,6 +128,7 @@
 JRE_TOOL_MODULES += \
     jdk.jdwp.agent \
     jdk.pack \
+    jdk.jpackage \
     jdk.scripting.nashorn.shell \
     #
 
@@ -168,6 +169,7 @@
     jdk.naming.rmi \
     jdk.net \
     jdk.pack \
+    jdk.jpackage \
     jdk.rmic \
     jdk.scripting.nashorn \
     jdk.sctp \
@@ -227,6 +229,13 @@
 endif
 
 ################################################################################
+# jpackage is only on windows, macosx, and linux
+
+ifeq ($(filter $(OPENJDK_TARGET_OS), windows macosx linux), )
+  MODULES_FILTER += jdk.jpackage
+endif
+
+################################################################################
 # Module list macros
 
 # Use append so that the custom extension may add to these variables
--- a/make/common/NativeCompilation.gmk	Wed Feb 27 12:19:29 2019 -0800
+++ b/make/common/NativeCompilation.gmk	Wed Feb 27 20:45:51 2019 -0500
@@ -390,6 +390,7 @@
 #   ARFLAGS the archiver flags to be used
 #   OBJECT_DIR the directory where we store the object files
 #   OUTPUT_DIR the directory where the resulting binary is put
+#   SYMBOLS_DIR the directory where the debug symbols are put, defaults to OUTPUT_DIR
 #   INCLUDES only pick source from these directories
 #   EXCLUDES do not pick source from these directories
 #   INCLUDE_FILES only compile exactly these files!
@@ -500,8 +501,6 @@
   $$(call SetIfEmpty, $1_SYSROOT_CFLAGS, $$($$($1_TOOLCHAIN)_SYSROOT_CFLAGS))
   $$(call SetIfEmpty, $1_SYSROOT_LDFLAGS, $$($$($1_TOOLCHAIN)_SYSROOT_LDFLAGS))
 
-  # Make sure the dirs exist.
-  $$(call MakeDir, $$($1_OBJECT_DIR) $$($1_OUTPUT_DIR))
   $$(foreach d, $$($1_SRC), $$(if $$(wildcard $$d), , \
       $$(error SRC specified to SetupNativeCompilation $1 contains missing directory $$d)))
 
@@ -842,30 +841,31 @@
 
   ifeq ($$($1_COPY_DEBUG_SYMBOLS), true)
     ifneq ($$($1_DEBUG_SYMBOLS), false)
+      $$(call SetIfEmpty, $1_SYMBOLS_DIR, $$($1_OUTPUT_DIR))
       # Only copy debug symbols for dynamic libraries and programs.
       ifneq ($$($1_TYPE), STATIC_LIBRARY)
         # Generate debuginfo files.
         ifeq ($(call isTargetOs, windows), true)
-          $1_EXTRA_LDFLAGS += -debug "-pdb:$$($1_OUTPUT_DIR)/$$($1_NOSUFFIX).pdb" \
-              "-map:$$($1_OUTPUT_DIR)/$$($1_NOSUFFIX).map"
-          $1_DEBUGINFO_FILES := $$($1_OUTPUT_DIR)/$$($1_NOSUFFIX).pdb \
-              $$($1_OUTPUT_DIR)/$$($1_NOSUFFIX).map
+          $1_EXTRA_LDFLAGS += -debug "-pdb:$$($1_SYMBOLS_DIR)/$$($1_NOSUFFIX).pdb" \
+              "-map:$$($1_SYMBOLS_DIR)/$$($1_NOSUFFIX).map"
+          $1_DEBUGINFO_FILES := $$($1_SYMBOLS_DIR)/$$($1_NOSUFFIX).pdb \
+              $$($1_SYMBOLS_DIR)/$$($1_NOSUFFIX).map
 
         else ifeq ($(call isTargetOs, linux solaris), true)
-          $1_DEBUGINFO_FILES := $$($1_OUTPUT_DIR)/$$($1_NOSUFFIX).debuginfo
+          $1_DEBUGINFO_FILES := $$($1_SYMBOLS_DIR)/$$($1_NOSUFFIX).debuginfo
           # Setup the command line creating debuginfo files, to be run after linking.
           # It cannot be run separately since it updates the original target file
           $1_CREATE_DEBUGINFO_CMDS := \
               $$($1_OBJCOPY) --only-keep-debug $$($1_TARGET) $$($1_DEBUGINFO_FILES) $$(NEWLINE) \
-              $(CD) $$($1_OUTPUT_DIR) && \
+              $(CD) $$($1_SYMBOLS_DIR) && \
                   $$($1_OBJCOPY) --add-gnu-debuglink=$$($1_DEBUGINFO_FILES) $$($1_TARGET)
 
         else ifeq ($(call isTargetOs, macosx), true)
           $1_DEBUGINFO_FILES := \
-              $$($1_OUTPUT_DIR)/$$($1_BASENAME).dSYM/Contents/Info.plist \
-              $$($1_OUTPUT_DIR)/$$($1_BASENAME).dSYM/Contents/Resources/DWARF/$$($1_BASENAME)
+              $$($1_SYMBOLS_DIR)/$$($1_BASENAME).dSYM/Contents/Info.plist \
+              $$($1_SYMBOLS_DIR)/$$($1_BASENAME).dSYM/Contents/Resources/DWARF/$$($1_BASENAME)
           $1_CREATE_DEBUGINFO_CMDS := \
-              $(DSYMUTIL) --out $$($1_OUTPUT_DIR)/$$($1_BASENAME).dSYM $$($1_TARGET)
+              $(DSYMUTIL) --out $$($1_SYMBOLS_DIR)/$$($1_BASENAME).dSYM $$($1_TARGET)
         endif
 
         # Since the link rule creates more than one file that we want to track,
@@ -887,14 +887,14 @@
         $1 += $$($1_DEBUGINFO_FILES)
 
         ifeq ($$($1_ZIP_EXTERNAL_DEBUG_SYMBOLS), true)
-          $1_DEBUGINFO_ZIP := $$($1_OUTPUT_DIR)/$$($1_NOSUFFIX).diz
+          $1_DEBUGINFO_ZIP := $$($1_SYMBOLS_DIR)/$$($1_NOSUFFIX).diz
           $1 += $$($1_DEBUGINFO_ZIP)
 
           # The dependency on TARGET is needed for debuginfo files
           # to be rebuilt properly.
           $$($1_DEBUGINFO_ZIP): $$($1_DEBUGINFO_FILES) $$($1_TARGET)
-		$(CD) $$($1_OUTPUT_DIR) && \
-		    $(ZIPEXE) -q -r $$@ $$(subst $$($1_OUTPUT_DIR)/,, $$($1_DEBUGINFO_FILES))
+		$(CD) $$($1_SYMBOLS_DIR) && \
+		    $(ZIPEXE) -q -r $$@ $$(subst $$($1_SYMBOLS_DIR)/,, $$($1_DEBUGINFO_FILES))
 
         endif
        endif # !STATIC_LIBRARY
@@ -930,6 +930,7 @@
 
     $$($1_TARGET): $$($1_TARGET_DEPS)
 	$$(call LogInfo, Building static library $$($1_BASENAME))
+	$$(call MakeDir, $$($1_OUTPUT_DIR) $$($1_SYMBOLS_DIR))
 	$$(call ExecuteWithLog, $$($1_OBJECT_DIR)/$$($1_SAFE_NAME)_link, \
 	    $$($1_AR) $$($1_ARFLAGS) $(AR_OUT_OPTION)$$($1_TARGET) $$($1_ALL_OBJS) \
 	        $$($1_RES))
@@ -1032,7 +1033,9 @@
                 # Keep as much as possible on one execution line for best performance
                 # on Windows
 		$$(call LogInfo, Linking $$($1_BASENAME))
+		$$(call MakeDir, $$($1_OUTPUT_DIR) $$($1_SYMBOLS_DIR))
                 ifeq ($(call isTargetOs, windows), true)
+
 		  $$(call ExecuteWithLog, $$($1_OBJECT_DIR)/$$($1_SAFE_NAME)_link, \
 		      $$($1_LD) $$($1_LDFLAGS) $$($1_EXTRA_LDFLAGS) $$($1_SYSROOT_LDFLAGS) \
 		          $(LD_OUT_OPTION)$$($1_TARGET) $$($1_LD_OBJ_ARG) $$($1_RES) $$(GLOBAL_LIBS) \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/make/launcher/Launcher-jdk.jpackage.gmk	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,78 @@
+#
+# Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+# particular file as subject to the "Classpath" exception as provided
+# by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+# or visit www.oracle.com if you need additional information or have any
+# questions.
+#
+
+include LauncherCommon.gmk
+
+
+################################################################################
+
+$(eval $(call SetupBuildLauncher, jpackage, \
+    MAIN_CLASS := jdk.jpackage.main.Main, \
+))
+
+################################################################################
+
+JPACKAGE_APPLAUNCHEREXE_SRC := \
+    $(TOPDIR)/src/jdk.jpackage/$(OPENJDK_TARGET_OS)/native/jpackageapplauncher
+
+# Output app launcher executable in resources dir, and symbols in the object dir
+$(eval $(call SetupJdkExecutable, BUILD_JPACKAGE_APPLAUNCHEREXE, \
+    NAME := jpackageapplauncher, \
+    OUTPUT_DIR := $(JDK_OUTPUTDIR)/modules/$(MODULE)/jdk/jpackage/internal/resources, \
+    SYMBOLS_DIR := $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/jpackageapplauncher, \
+    SRC := $(JPACKAGE_APPLAUNCHEREXE_SRC), \
+    TOOLCHAIN := TOOLCHAIN_LINK_CXX, \
+    OPTIMIZATION := LOW, \
+    CFLAGS := $(CXXFLAGS_JDKEXE), \
+    CFLAGS_windows := -EHsc -DLAUNCHERC -DUNICODE -D_UNICODE, \
+    LDFLAGS := $(LDFLAGS_JDKEXE), \
+    LIBS_macosx := -framework Cocoa, \
+    LIBS := $(LIBCXX), \
+    LIBS_linux := -ldl, \
+    LIBS_windows :=  user32.lib shell32.lib advapi32.lib, \
+))
+
+TARGETS += $(BUILD_JPACKAGE_APPLAUNCHEREXE)
+
+# Build non-console version of launcher
+ifeq ($(OPENJDK_TARGET_OS), windows)
+
+  $(eval $(call SetupJdkExecutable, BUILD_JPACKAGE_APPLAUNCHERWEXE, \
+      NAME := jpackageapplauncherw, \
+      OUTPUT_DIR := $(JDK_OUTPUTDIR)/modules/$(MODULE)/jdk/jpackage/internal/resources, \
+      SYMBOLS_DIR := $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/jpackageapplauncherw, \
+      SRC := $(JPACKAGE_APPLAUNCHEREXE_SRC), \
+      TOOLCHAIN := TOOLCHAIN_LINK_CXX, \
+      OPTIMIZATION := LOW, \
+      CFLAGS := $(CXXFLAGS_JDKEXE), \
+      CFLAGS_windows := -EHsc -DUNICODE -D_UNICODE, \
+      LDFLAGS := $(LDFLAGS_JDKEXE), \
+      LIBS := $(LIBCXX), \
+      LIBS_windows :=  user32.lib shell32.lib advapi32.lib, \
+  ))
+
+  TARGETS += $(BUILD_JPACKAGE_APPLAUNCHERWEXE)
+endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/make/lib/Lib-jdk.jpackage.gmk	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,69 @@
+#
+# Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+# particular file as subject to the "Classpath" exception as provided
+# by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+# or visit www.oracle.com if you need additional information or have any
+# questions.
+#
+
+include LibCommon.gmk
+
+################################################################################
+
+# Output app launcher library in resources dir, and symbols in the object dir
+$(eval $(call SetupJdkLibrary, BUILD_LIB_APPLAUNCHER, \
+    NAME := applauncher, \
+    OUTPUT_DIR := $(JDK_OUTPUTDIR)/modules/$(MODULE)/jdk/jpackage/internal/resources, \
+    SYMBOLS_DIR := $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/libapplauncher, \
+    TOOLCHAIN := TOOLCHAIN_LINK_CXX, \
+    OPTIMIZATION := LOW, \
+    CFLAGS := $(CXXFLAGS_JDKLIB), \
+    CFLAGS_windows := -EHsc -DUNICODE -D_UNICODE, \
+    LDFLAGS := $(LDFLAGS_JDKLIB) $(LDFLAGS_CXX_JDK) \
+        $(call SET_SHARED_LIBRARY_ORIGIN), \
+    LIBS := $(LIBCXX), \
+    LIBS_windows := user32.lib shell32.lib advapi32.lib ole32.lib, \
+    LIBS_linux := -ldl -lpthread, \
+    LIBS_macosx := -ldl -framework Cocoa, \
+))
+
+$(BUILD_LIB_APPLAUNCHER): $(call FindLib, java.base, java)
+
+TARGETS += $(BUILD_LIB_APPLAUNCHER)
+
+################################################################################
+
+ifeq ($(OPENJDK_TARGET_OS), windows)
+
+  $(eval $(call SetupJdkLibrary, BUILD_LIB_JPACKAGE, \
+      NAME := jpackage, \
+      OPTIMIZATION := LOW, \
+      CFLAGS := $(CXXFLAGS_JDKLIB), \
+      CFLAGS_windows := -EHsc -DUNICODE -D_UNICODE, \
+      LDFLAGS := $(LDFLAGS_JDKLIB) $(LDFLAGS_CXX_JDK) \
+          $(call SET_SHARED_LIBRARY_ORIGIN), \
+      LIBS := $(LIBCXX), \
+      LIBS_windows := user32.lib shell32.lib advapi32.lib ole32.lib, \
+  ))
+
+  TARGETS += $(BUILD_LIB_JPACKAGE)
+
+endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackage/JNLPConverter/README.txt	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,23 @@
+About JNLPConverter
+===================
+
+JNLPConverter is a standalone tool which uses jpackage to create bundles from
+Java Web Start(TM) Applications and helps to migrate from JNLP to jpackage.
+JNLPConverter will locate and use the jpackage tool from the same JDK as used
+to run JNLPConverter. JNLPConverter supports HTTP/HTTPS and FILE protocol.
+
+Running JNLPConverter
+=====================
+
+To run the JNLPConverter:
+
+  java -jar JNLPConverter.jar <mode> <options>
+
+To get help on JNLPConverter options:
+
+  java -jar JNLPConverter.jar --help
+
+These instructions assume that this installation's version of the java command
+is in your path. If it isn't, then you should either specify the complete path
+to the java command or update your PATH environment variable as described
+in the installation instructions for the Java(TM) SE Development Kit.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/HTTPHelper.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,193 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jnlp.converter;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import jnlp.converter.parser.GeneralUtil;
+
+public class HTTPHelper {
+
+    public static final int BUFFER_SIZE = 4096;
+
+    public static String downloadFile(String url, String destFolder, String destFileName) throws MalformedURLException, IOException {
+        HttpURLConnection connection = null;
+        String destFile = null;
+
+        try {
+            if (url.contains(" ")) {
+                url = url.replace(" ", "%20");
+            }
+            if (url.contains("\\")) {
+                url = url.replace("\\", "/");
+            }
+
+            URL resource = new URL(url);
+            connection = (HttpURLConnection) resource.openConnection();
+
+            int responseCode = connection.getResponseCode();
+            if (responseCode == HttpURLConnection.HTTP_OK) {
+                destFile = destFolder + File.separator + destFileName;
+                Log.verbose("Downloading " + url + " to " + destFile);
+
+                try (InputStream inputStream = connection.getInputStream();
+                     OutputStream outputStream = new FileOutputStream(destFile)) {
+                    byte[] buffer = new byte[BUFFER_SIZE];
+
+                    int length;
+                    do {
+                        length = inputStream.read(buffer);
+                        if (length > 0) {
+                            outputStream.write(buffer, 0, length);
+                        }
+                    } while (length > 0);
+                }
+            } else {
+                HTTPHelperException e = new HTTPHelperException("Error: Cannot download " + url + ". Server response code: " + responseCode);
+                e.setResponseCode(responseCode);
+                throw e;
+            }
+        } catch (IOException e) {
+            if (e instanceof HTTPHelperException) {
+                throw e;
+            } else {
+                throw new HTTPHelperException("Error: Cannot download " + url + ". " + e.getClass().getSimpleName() + ": " + e.getMessage());
+            }
+        } finally {
+            if (connection != null) {
+                connection.disconnect();
+            }
+        }
+
+        return destFile;
+    }
+
+    public static String copyFile(String url, String destFolder, String destFileName) throws Exception {
+        if (url.contains(" ")) {
+            url = url.replace(" ", "%20");
+        }
+
+        URI sourceURI = new URI(url);
+
+        String sourceFile = sourceURI.getPath();
+        File file = new File(sourceFile);
+        if (!file.exists()) {
+            throw new FileNotFoundException("Error: " + sourceFile + " does not exist.");
+        }
+
+        String destFile = destFolder + File.separator + destFileName;
+        file = new File(destFile);
+        if (file.exists()) {
+            file.delete();
+        }
+
+        Path sourcePath = Paths.get(sourceURI);
+        Path destPath = Paths.get(destFile);
+        Log.verbose("Copying " + url + " to " + destFile);
+        Files.copy(sourcePath, destPath);
+
+        return destFile;
+    }
+
+    public static boolean isHTTPUrl(String url) {
+        return (url.startsWith("http://") || url.startsWith("https://"));
+    }
+
+    public static byte[] getJNLPBits(String versionedJNLP, String jnlp) throws Exception {
+        String jnlpFilePath = null;
+        byte[] bits = null;
+
+        if (isHTTPUrl(jnlp)) {
+            try {
+                jnlpFilePath = downloadFile(versionedJNLP, JNLPConverter.getJnlpDownloadFolderStatic(), getFileNameFromURL(jnlp));
+            } catch (HTTPHelperException ex) {
+                if (ex.getResponseCode() == HttpURLConnection.HTTP_NOT_FOUND &&
+                       !versionedJNLP.equals(jnlp)) {
+                    Log.warning("Downloading versioned JNLP from " + versionedJNLP + " failed.");
+                    Log.warning(ex.getMessage());
+                    Log.warning("Downloading " + jnlp + " instead.");
+                    jnlpFilePath = downloadFile(jnlp, JNLPConverter.getJnlpDownloadFolderStatic(), getFileNameFromURL(jnlp));
+                } else {
+                    throw ex;
+                }
+            }
+            JNLPConverter.markFileToDelete(jnlpFilePath);
+        } else {
+            try {
+                jnlpFilePath = copyFile(versionedJNLP, JNLPConverter.getJnlpDownloadFolderStatic(), getFileNameFromURL(jnlp));
+            } catch (FileNotFoundException ex) {
+                System.out.println("Error copying versioned JNLP from " + versionedJNLP);
+                System.out.println(ex.getMessage());
+                System.out.println("Copying " + jnlp + " instead.");
+                jnlpFilePath = HTTPHelper.copyFile(jnlp, JNLPConverter.getJnlpDownloadFolderStatic(), getFileNameFromURL(jnlp));
+            }
+            JNLPConverter.markFileToDelete(jnlpFilePath);
+        }
+
+        File jnlpFile = new File(jnlpFilePath);
+        if (jnlpFile.exists()) {
+            bits = GeneralUtil.readBytes(new FileInputStream(jnlpFile), jnlpFile.length());
+        }
+
+        return bits;
+    }
+
+    public static String getFileNameFromURL(String url) throws IOException {
+        int index;
+        int index1 = url.lastIndexOf('/');
+        int index2 = url.lastIndexOf('\\');
+
+        if (index1 >= index2) {
+            index = index1;
+        } else {
+            index = index2;
+        }
+
+        if (index != -1) {
+            String name = url.substring(index + 1, url.length());
+            name = name.replace("%20", " ");
+            if (name.endsWith(".jnlp") || name.endsWith(".jar")) { // JNLP or JAR
+                return name;
+            } else if (name.endsWith(".ico")) { // Icons
+                return name;
+            } else {
+                throw new IOException("Error: Unsupported file extension for " + url);
+            }
+        } else {
+            throw new IOException("Error: URL (" + url + ") should end with file name.");
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/HTTPHelperException.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jnlp.converter;
+
+import java.io.IOException;
+
+public class HTTPHelperException extends IOException {
+    private int responseCode = -1;
+
+    public HTTPHelperException(String msg) {
+        super(msg);
+    }
+
+    public void setResponseCode(int code) {
+        responseCode = code;
+    }
+
+    public int getResponseCode() {
+        return responseCode;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/JNLPConverter.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,865 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jnlp.converter;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.net.HttpURLConnection;
+import java.net.URI;
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import jnlp.converter.parser.JNLPDesc;
+import jnlp.converter.parser.JNLPDesc.AssociationDesc;
+import jnlp.converter.parser.JNLPDesc.IconDesc;
+import jnlp.converter.parser.ResourcesDesc.JARDesc;
+import jnlp.converter.parser.XMLFormat;
+
+public class JNLPConverter {
+
+    private final Options options;
+    private JNLPDesc jnlpd = null;
+    private final List<String> launchArgs = new ArrayList<>();
+
+    private String downloadFolder = null;
+    private String jnlpDownloadFolder = null;
+    private static String jnlpDownloadFolderStatic;
+    private String jarDownloadFolder = null;
+    private String iconDownloadFolder = null;
+    private String propDownloadFolder = null;
+
+    private static String jpackagePath = null;
+
+    private static boolean markFileToDelete = false;
+
+    private static final String FA_EXTENSIONS = "extension";
+    private static final String FA_CONTENT_TYPE = "mime-type";
+    private static final String FA_DESCRIPTION = "description";
+    private static final String FA_ICON = "icon";
+
+    public JNLPConverter(Options options) {
+        this.options = options;
+        jnlpDownloadFolderStatic = getJnlpDownloadFolder();
+        markFileToDelete = (options.keep() == null);
+    }
+
+    public String [] getLaunchArgs() {
+        return launchArgs.toArray(new String[0]);
+    }
+
+    public void convert() {
+        try {
+            loadJNLPDesc();
+            downloadResources();
+            validate();
+            buildLaunchArgs();
+            saveLaunchArgs();
+            runJPackage();
+        } catch (Exception ex) {
+            Log.error(ex.getLocalizedMessage());
+        }
+    }
+
+    private JNLPDesc getJNLPD(String jnlp) throws Exception {
+        URL codebase = getCodeBase(jnlp);
+        byte[] bits = HTTPHelper.getJNLPBits(jnlp, jnlp);
+        return XMLFormat.parse(bits, codebase, jnlp);
+    }
+
+    private void loadJNLPDesc() throws Exception {
+        String jnlp = options.getJNLP();
+        jnlpd = getJNLPD(jnlp);
+
+        // Check for required options in case of FX
+        if (jnlpd.isFXApp()) {
+            if (!options.isRuntimeImageSet()) {
+                throw new Exception("This is a JavaFX Web-Start application which requires a runtime image capable of running JavaFX applications, which can be specified by the jpackage option --runtime-image (using --jpackage-options).");
+            }
+        }
+
+        // Check href. It can be same as URL we provided or new one
+        // if JNLP has different href or codebase. We assume that
+        // XMLFormat.parse() will handle any errors in href and codebase
+        // correctly.
+        String href = jnlpd.getHref();
+        if (href != null && !href.equalsIgnoreCase(jnlp)) {
+            if (href.startsWith("file:")) {
+                URI hrefURI = new URI(href);
+                URI jnlpURI = new URI(jnlp);
+
+                String hrefPath = hrefURI.getPath();
+                String jnlpPath = jnlpURI.getPath();
+
+                if (!hrefPath.equalsIgnoreCase(jnlpPath)) {
+                    jnlp = href;
+                    jnlpd = getJNLPD(jnlp);
+                }
+            } else {
+                jnlp = href;
+                jnlpd = getJNLPD(jnlp);
+            }
+        }
+
+        if (jnlpd.getName() == null) {
+            jnlpd.setName(getNameFromURL(jnlp));
+        }
+    }
+
+    private static String getNameFromURL(String url) throws IOException {
+        int index;
+        int index1 = url.lastIndexOf('/');
+        int index2 = url.lastIndexOf('\\');
+
+        if (index1 >= index2) {
+            index = index1;
+        } else {
+            index = index2;
+        }
+
+        if (index != -1) {
+            String name = url.substring(index + 1, url.length());
+            if (name.endsWith(".jnlp")) {
+                return name.substring(0, name.length() - 5);
+            }
+        }
+
+        return null;
+    }
+
+    private URL getCodeBase(String jnlp) throws Exception {
+        int index = jnlp.lastIndexOf('/');
+        if (index != -1) {
+            if (HTTPHelper.isHTTPUrl(jnlp)) {
+                return new URL(jnlp.substring(0, index + 1));
+            } else {
+                String codeBasePath = jnlp.substring(0, index);
+                if (!codeBasePath.endsWith("/")) {
+                    codeBasePath += "/";
+                }
+                return new URI(codeBasePath).toURL();
+            }
+        }
+
+        return null;
+    }
+
+    public static void markFileToDelete(String file) {
+        if (file == null || file.isEmpty()) {
+            return;
+        }
+
+        if (markFileToDelete) {
+            try {
+                File f = new File(file);
+                f.deleteOnExit();
+            } catch (Exception e) {
+                // Print exception, but do not fail conversion.
+                Log.warning(e.getLocalizedMessage());
+            }
+        }
+    }
+
+    public static void deleteFile(String file) {
+        try {
+            File f = new File(file);
+            f.delete();
+        } catch (Exception e) {
+            Log.warning(e.getLocalizedMessage());
+        }
+    }
+
+    private void downloadResources() throws Exception {
+        List<JARDesc> jars = jnlpd.getResources();
+        for (JARDesc jar : jars) {
+            if (jar.getVersion() != null) {
+                if (!jnlpd.isVersionEnabled()) {
+                    throw new Exception("Error: Version based download protocol is not supported without -Djnlp.versionEnabled=true.");
+                }
+            }
+
+            String destFile = null;
+            if (HTTPHelper.isHTTPUrl(jar.getLocation().toString())) {
+                if (jar.getVersion() != null) {
+                    try {
+                        destFile = HTTPHelper.downloadFile(jar.getVersionLocation().toString(), getJarDownloadFolder(), HTTPHelper.getFileNameFromURL(jar.getLocation().toString()));
+                    } catch (HTTPHelperException ex) {
+                        if (ex.getResponseCode() == HttpURLConnection.HTTP_NOT_FOUND) {
+                            System.out.println("Error downloading versioned JAR from " + jar.getVersionLocation());
+                            System.out.println(ex.getMessage());
+                            System.out.println("Downloading " + jar.getLocation() + " instead.");
+                            destFile = HTTPHelper.downloadFile(jar.getLocation().toString(), getJarDownloadFolder(), HTTPHelper.getFileNameFromURL(jar.getLocation().toString()));
+                        } else {
+                            throw ex;
+                        }
+                    }
+                } else {
+                    destFile = HTTPHelper.downloadFile(jar.getLocation().toString(), getJarDownloadFolder(), HTTPHelper.getFileNameFromURL(jar.getLocation().toString()));
+                }
+                markFileToDelete(destFile);
+            } else {
+                if (jar.getVersion() != null) {
+                    try {
+                        destFile = HTTPHelper.copyFile(jar.getVersionLocation().toString(), getJarDownloadFolder(), HTTPHelper.getFileNameFromURL(jar.getLocation().toString()));
+                    } catch (FileNotFoundException ex) {
+                        System.out.println("Error copying versioned JAR from " + jar.getVersionLocation());
+                        System.out.println(ex.getMessage());
+                        System.out.println("Copying " + jar.getLocation() + " instead.");
+                        destFile = HTTPHelper.copyFile(jar.getLocation().toString(), getJarDownloadFolder(), HTTPHelper.getFileNameFromURL(jar.getLocation().toString()));
+                    }
+                } else {
+                    destFile = HTTPHelper.copyFile(jar.getLocation().toString(), getJarDownloadFolder(), HTTPHelper.getFileNameFromURL(jar.getLocation().toString()));
+                }
+                markFileToDelete(destFile);
+            }
+
+            if (jar.isNativeLib()) {
+                unpackNativeLib(destFile);
+                deleteFile(destFile);
+            } else {
+                jnlpd.addFile(jar.getName());
+            }
+        }
+
+        IconDesc icon = jnlpd.getIcon();
+        if (icon != null) {
+            String destFile;
+
+            if (HTTPHelper.isHTTPUrl(icon.getLocation())) {
+                destFile = HTTPHelper.downloadFile(icon.getLocation(), getIconDownloadFolder(), HTTPHelper.getFileNameFromURL(icon.getLocation()));
+            } else {
+                destFile = HTTPHelper.copyFile(icon.getLocation(), getIconDownloadFolder(), HTTPHelper.getFileNameFromURL(icon.getLocation()));
+            }
+
+            markFileToDelete(destFile);
+            icon.setLocalLocation(destFile);
+        }
+
+        AssociationDesc [] associations = jnlpd.getAssociations();
+        if (associations != null) {
+            for (AssociationDesc association : associations) {
+                if (association.getIconUrl() != null) {
+                    String destFile;
+                    if (HTTPHelper.isHTTPUrl(association.getIconUrl())) {
+                        destFile = HTTPHelper.downloadFile(association.getIconUrl(), getIconDownloadFolder(), HTTPHelper.getFileNameFromURL(association.getIconUrl()));
+                    } else {
+                        destFile = HTTPHelper.copyFile(association.getIconUrl(), getIconDownloadFolder(), HTTPHelper.getFileNameFromURL(association.getIconUrl()));
+                    }
+
+                    markFileToDelete(destFile);
+                    association.setIconLocalLocation(destFile);
+                }
+            }
+        }
+    }
+
+    public void unpackNativeLib(String file) throws IOException {
+        try (JarFile jarFile = new JarFile(file)) {
+            Enumeration entries = jarFile.entries();
+
+            while (entries.hasMoreElements()) {
+                JarEntry entry = (JarEntry) entries.nextElement();
+
+                // Skip directories
+                if (entry.isDirectory()) {
+                    continue;
+                }
+
+                String entryName = entry.getName();
+                // Skip anything in sub-directories
+                if (entryName.contains("\\") || entryName.contains("/")) {
+                    continue;
+                }
+
+                // Skip anything not ending with .dll, .dylib or .so
+                if (!entryName.endsWith(".dll") && !entryName.endsWith(".dylib") && !entryName.endsWith(".so")) {
+                    continue;
+                }
+
+                File destFile = new File(getJarDownloadFolder(), entryName);
+                if (destFile.exists()) {
+                    Log.warning(destFile.getAbsolutePath() + " already exist and will not be overwriten by native library from " + file + ".");
+                    continue;
+                }
+
+                InputStream inputStream = jarFile.getInputStream(entry);
+                FileOutputStream outputStream = new FileOutputStream(destFile);
+
+                byte[] buffer = new byte[HTTPHelper.BUFFER_SIZE];
+                int length;
+                do {
+                    length = inputStream.read(buffer);
+                    if (length > 0) {
+                        outputStream.write(buffer, 0, length);
+                    }
+                } while (length > 0);
+
+                jnlpd.addFile(entryName);
+            }
+        }
+    }
+
+    private void validate() {
+        if (jnlpd.getMainJar() == null) {
+            Log.error("Cannot find main jar");
+        }
+
+        if (jnlpd.getMainClass() == null) {
+            Log.error("Cannot find main class");
+        }
+    }
+
+    private void addLaunchArg(String arg, List<String> launchArgs) {
+        if (arg != null && !arg.isEmpty()) {
+            if (!options.isOptionPresent(arg)){
+                launchArgs.add(arg);
+            } else {
+                Log.info(arg + " generated by JNLPConverter is dropped, since it is overwriten via --jpackage-options");
+            }
+        }
+    }
+
+    private void addLaunchArg(String arg, String value, List<String> launchArgs) {
+        if (arg != null && !arg.isEmpty() && value != null && !value.isEmpty()) {
+            if (!options.isOptionPresent(arg)){
+                launchArgs.add(arg);
+                launchArgs.add(value);
+            } else {
+                Log.info(arg + "=" + value +" generated by JNLPConverter is dropped, since it is overwriten via --jpackage-options");
+            }
+        }
+    }
+
+    private void displayLaunchArgs() {
+        if (Log.isVerbose()) {
+            System.out.println();
+            System.out.println("jpackage launch arguments (each argument starts on new line):");
+            launchArgs.forEach((arg) -> {
+                System.out.println(arg);
+            });
+        }
+    }
+
+    private static int fileAssociationsCount = 0;
+    private String getFileAssociationsFile() {
+        String file = getPropDownloadFolder();
+        file += File.separator;
+        file += "fileAssociation";
+        file += String.valueOf(fileAssociationsCount);
+        file += ".properties";
+
+        fileAssociationsCount++;
+
+        return file;
+    }
+
+    private void buildLaunchArgs() {
+        if (options.createImage()) {
+            addLaunchArg("create-image", launchArgs);
+        } else if (options.createInstaller()) {
+            if (options.getInstallerType() == null) {
+                addLaunchArg("create-installer", launchArgs);
+            } else {
+                addLaunchArg("create-installer", options.getInstallerType(), launchArgs);
+            }
+        }
+
+        // Set verbose for jpackage if it is set for us.
+        if (options.verbose()) {
+            addLaunchArg("--verbose", launchArgs);
+        }
+
+        addLaunchArg("--input", getJarDownloadFolder(), launchArgs);
+        addLaunchArg("--output", options.getOutput(), launchArgs);
+        addLaunchArg("--name", jnlpd.getName(), launchArgs);
+        addLaunchArg("--version", jnlpd.getVersion(), launchArgs);
+        addLaunchArg("--vendor", jnlpd.getVendor(), launchArgs);
+        addLaunchArg("--description", jnlpd.getDescription(), launchArgs);
+        addLaunchArg("--icon", jnlpd.getIconLocation(), launchArgs);
+        addLaunchArg("--main-jar", jnlpd.getMainJar(), launchArgs);
+        addLaunchArg("--class", jnlpd.getMainClass(), launchArgs);
+
+        addFiles(launchArgs);
+        addArguments(launchArgs);
+        addJVMArgs(launchArgs);
+
+        if (jnlpd.isDesktopHint()) {
+            if (Platform.isWindows()) {
+                addLaunchArg("--win-shortcut", launchArgs);
+            } else {
+                Log.warning("Ignoring shortcut hint, since it is not supported on current platform.");
+            }
+        }
+
+        if (jnlpd.isMenuHint()) {
+            if (Platform.isWindows()) {
+                addLaunchArg("--win-menu", launchArgs);
+                addLaunchArg("--win-menu-group", jnlpd.getSubMenu(), launchArgs);
+            } else {
+                Log.warning("Ignoring menu hint, since it is not supported on current platform.");
+            }
+        }
+
+        AssociationDesc [] associations = jnlpd.getAssociations();
+        if (associations != null) {
+            for (AssociationDesc association : associations) {
+                String file = getFileAssociationsFile();
+                markFileToDelete(file);
+
+                try (PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(file)))) {
+                    if (association.getExtensions() != null && association.getMimeType() != null) {
+                        out.println(FA_EXTENSIONS + "=" + quote(association.getExtensions()));
+                        out.println(FA_CONTENT_TYPE + "=" + quote(association.getMimeType()));
+
+                        if (association.getMimeDescription() != null) {
+                            out.println(FA_DESCRIPTION + "=" + association.getMimeDescription());
+                        }
+
+                        if (association.getIconLocalLocation() != null) {
+                            out.println(FA_ICON + "=" + quote(association.getIconLocalLocation()));
+                        }
+
+                        addLaunchArg("--file-associations", file, launchArgs);
+                    }
+                } catch (Exception ex) {
+                    Log.warning(ex.toString());
+                    if (association.getExtensions() != null) {
+                        Log.warning("File assoication for " + association.getExtensions() + " will be ignored due to exception above.");
+                    }
+                }
+            }
+        }
+
+        // Add options from --jpackage-options
+        List<String> jpackageOptions = options.getJPackageOptions();
+        jpackageOptions.forEach((option) -> {
+            launchArgs.add(option);
+        });
+
+        displayLaunchArgs();
+    }
+
+    private String getCommandFileName() {
+        Platform platform = Platform.getPlatform();
+        switch (platform) {
+            case WINDOWS:
+                return "run_jpackage.bat";
+            case LINUX:
+                return "run_jpackage.sh";
+            case MAC:
+                return "run_jpackage.sh";
+            default:
+                Log.error("Cannot determine platform type.");
+                return "";
+        }
+    }
+
+    private void saveLaunchArgs() {
+        if (options.keep() != null) {
+            File keepFolder = new File(options.keep());
+            String cmdFile = keepFolder.getAbsolutePath() + File.separator + getCommandFileName();
+            try (PrintWriter out = new PrintWriter(cmdFile)) {
+                out.print(getJPackagePath());
+                launchArgs.forEach((arg) -> {
+                    out.print(" ");
+
+                    if (arg.contains(" ")) {
+                        int len = arg.length();
+                        if (len >= 1) {
+                            if (arg.charAt(0) != '"' && arg.charAt(len - 1) != '"') {
+                                out.print("\"" + arg + "\"");
+                            } else {
+                                if (Platform.isWindows()) {
+                                    out.print(arg);
+                                } else {
+                                    arg = escapeQuote(arg);
+                                    out.print("\"" + arg + "\"");
+                                }
+                            }
+                        }
+                    } else {
+                        out.print(arg);
+                    }
+                });
+            } catch (FileNotFoundException ex) {
+                Log.error("Cannot save file with command line: " + ex.getLocalizedMessage());
+            }
+        }
+    }
+
+    private void runJPackage() {
+        List<String> command = new ArrayList<>();
+        command.add(getJPackagePath());
+        command.addAll(launchArgs);
+
+        ProcessBuilder builder = new ProcessBuilder();
+        builder.inheritIO();
+        builder.command(command);
+
+        try {
+            Process process = builder.start();
+            int exitCode = process.waitFor();
+            if (exitCode != 0) {
+                Log.warning("jpackage retrun non zero code: " + exitCode);
+            }
+        } catch (IOException | InterruptedException ex) {
+            Log.error(ex.getMessage());
+        }
+    }
+
+    private void addFileList(String arg, List<String> filesToAdd, List<String> launchArgs) {
+        if (filesToAdd.isEmpty()) {
+            return;
+        }
+
+        String filesArg = "";
+        for (int i = 0; i < filesToAdd.size(); i++) {
+            filesArg += quote(filesToAdd.get(i));
+            if ((i + 1) != filesToAdd.size()) {
+                filesArg += File.pathSeparator;
+            }
+        }
+
+        launchArgs.add(arg);
+        launchArgs.add(filesArg);
+    }
+
+    private void addFiles(List<String> launchArgs) {
+        addFileList("--files", jnlpd.getFiles(), launchArgs);
+    }
+
+    private void addArguments(List<String> launchArgs) {
+        List<String> arguments = jnlpd.getArguments();
+        if (arguments.isEmpty()) {
+            return;
+        }
+
+        String argsStr = "";
+        for (int i = 0; i < arguments.size(); i++) {
+            String arg = arguments.get(i);
+            argsStr += quote(arg);
+            if ((i + 1) != arguments.size()) {
+                argsStr += " ";
+            }
+        }
+
+        launchArgs.add("--arguments");
+        if (Platform.isWindows()) {
+            if (argsStr.contains(" ")) {
+                if (argsStr.contains("\"")) {
+                    argsStr = escapeQuote(argsStr);
+                }
+                argsStr = "\"" + argsStr + "\"";
+            }
+        }
+        launchArgs.add(argsStr);
+    }
+
+    private void addJVMArgs(List<String> launchArgs) {
+        List<String> jvmArgs = jnlpd.getVMArgs();
+        if (jvmArgs.isEmpty()) {
+            return;
+        }
+
+        String jvmArgsStr = "";
+        for (int i = 0; i < jvmArgs.size(); i++) {
+            String arg = jvmArgs.get(i);
+            jvmArgsStr += quote(arg);
+            if ((i + 1) != jvmArgs.size()) {
+                jvmArgsStr += " ";
+            }
+        }
+
+        launchArgs.add("--jvm-args");
+        if (Platform.isWindows()) {
+            if (jvmArgsStr.contains(" ")) {
+                if (jvmArgsStr.contains("\"")) {
+                    jvmArgsStr = escapeQuote(jvmArgsStr);
+                }
+                jvmArgsStr = "\"" + jvmArgsStr + "\"";
+            }
+        }
+        launchArgs.add(jvmArgsStr);
+    }
+
+    private String quote(String in) {
+        if (in == null) {
+            return null;
+        }
+
+        if (in.isEmpty()) {
+            return "";
+        }
+
+        if (!in.contains("=")) {
+            // Not a property
+            if (in.contains(" ")) {
+                in = escapeQuote(in);
+                return "\"" + in + "\"";
+            }
+            return in;
+        }
+
+        if (!in.contains(" ")) {
+            return in; // No need to quote
+        }
+
+        int paramIndex = in.indexOf("=");
+        if (paramIndex <= 0) {
+            return in; // Something wrong, just skip quoting
+        }
+
+        String param = in.substring(0, paramIndex);
+        String value = in.substring(paramIndex + 1);
+
+        if (value.length() == 0) {
+            return in; // No need to quote
+        }
+
+        value = escapeQuote(value);
+
+        return param + "=" + "\"" + value + "\"";
+    }
+
+    private String escapeQuote(String in) {
+        if (in == null) {
+            return null;
+        }
+
+        if (in.isEmpty()) {
+            return "";
+        }
+
+        if (in.contains("\"")) {
+            // Use code points to preserve non-ASCII chars
+            StringBuilder sb = new StringBuilder();
+            int codeLen = in.codePointCount(0, in.length());
+            for (int i = 0; i < codeLen; i++) {
+                int code = in.codePointAt(i);
+                // Note: No need to escape '\' on Linux or OS X.
+                // jpackage expects us to pass arguments and properties with quotes and spaces as a map
+                // with quotes being escaped with additional \ for internal quotes.
+                // So if we want two properties below:
+                // -Djnlp.Prop1=Some "Value" 1
+                // -Djnlp.Prop2=Some Value 2
+                // jpackage will need:
+                // "-Djnlp.Prop1=\"Some \\"Value\\" 1\" -Djnlp.Prop2=\"Some Value 2\""
+                // but since we using ProcessBuilder to run jpackage we will need to escape
+                // our escape symbols as well, so we will need to pass string below to ProcessBuilder:
+                // "-Djnlp.Prop1=\\\"Some \\\\\\\"Value\\\\\\\" 1\\\" -Djnlp.Prop2=\\\"Some Value 2\\\""
+                switch (code) {
+                    case '"':
+                        // " -> \" -> \\\"
+                        if (i == 0 || in.codePointAt(i - 1) != '\\') {
+                            if (Platform.isWindows()) {
+                                sb.appendCodePoint('\\');
+                                sb.appendCodePoint('\\');
+                            }
+                            sb.appendCodePoint('\\');
+                            sb.appendCodePoint(code);
+                        }
+                        break;
+                    case '\\':
+                        // We need to escape already escaped symbols as well
+                        if ((i + 1) < codeLen) {
+                            int nextCode = in.codePointAt(i + 1);
+                            if (nextCode == '"') {
+                                // \" -> \\\"
+                                sb.appendCodePoint('\\');
+                                sb.appendCodePoint('\\');
+                                sb.appendCodePoint('\\');
+                                sb.appendCodePoint(nextCode);
+                            } else {
+                                sb.appendCodePoint('\\');
+                                sb.appendCodePoint(code);
+                            }
+                        } else {
+                            if (Platform.isWindows()) {
+                                sb.appendCodePoint('\\');
+                            }
+                            sb.appendCodePoint(code);
+                        }
+                        break;
+                    default:
+                        sb.appendCodePoint(code);
+                        break;
+                }
+            }
+            return sb.toString();
+        }
+
+        return in;
+    }
+
+    public synchronized String getDownloadFolder() {
+        if (downloadFolder == null) {
+            try {
+                File file;
+                if (options.keep() == null) {
+                    Path path = Files.createTempDirectory("JNLPConverter");
+                    file = path.toFile();
+                    file.deleteOnExit();
+                } else {
+                    file = new File(options.keep());
+                    if (!file.exists()) {
+                        file.mkdir();
+                    }
+                }
+
+                downloadFolder = file.getAbsolutePath();
+            } catch (IOException e) {
+                Log.error(e.getLocalizedMessage());
+            }
+        }
+
+        return downloadFolder;
+    }
+
+    public final synchronized String getJnlpDownloadFolder() {
+        if (jnlpDownloadFolder == null) {
+            File file = new File(getDownloadFolder() + File.separator + "jnlp");
+            file.mkdir();
+            markFileToDelete(getDownloadFolder() + File.separator + "jnlp");
+            jnlpDownloadFolder = file.getAbsolutePath();
+        }
+
+        return jnlpDownloadFolder;
+    }
+
+    public static String getJnlpDownloadFolderStatic() {
+        return jnlpDownloadFolderStatic;
+    }
+
+    public synchronized String getJarDownloadFolder() {
+        if (jarDownloadFolder == null) {
+            File file = new File(getDownloadFolder() + File.separator + "jar");
+            file.mkdir();
+            markFileToDelete(getDownloadFolder() + File.separator + "jar");
+            jarDownloadFolder = file.getAbsolutePath();
+        }
+
+        return jarDownloadFolder;
+    }
+
+    public synchronized String getIconDownloadFolder() {
+        if (iconDownloadFolder == null) {
+            File file = new File(getDownloadFolder() + File.separator + "icon");
+            file.mkdir();
+            markFileToDelete(getDownloadFolder() + File.separator + "icon");
+            iconDownloadFolder = file.getAbsolutePath();
+        }
+
+        return iconDownloadFolder;
+    }
+
+    public synchronized String getPropDownloadFolder() {
+        if (propDownloadFolder == null) {
+            File file = new File(getDownloadFolder() + File.separator + "prop");
+            file.mkdir();
+            markFileToDelete(getDownloadFolder() + File.separator + "prop");
+            propDownloadFolder = file.getAbsolutePath();
+        }
+
+        return propDownloadFolder;
+    }
+
+    public synchronized static String getJPackagePath() {
+        if (jpackagePath == null) {
+            jpackagePath = System.getProperty("java.home");
+            jpackagePath += File.separator;
+            jpackagePath += "bin";
+            jpackagePath += File.separator;
+
+            Platform platform = Platform.getPlatform();
+            switch (platform) {
+                case WINDOWS:
+                    jpackagePath += "jpackage.exe";
+                    break;
+                case LINUX:
+                    jpackagePath += "jpackage";
+                    break;
+                case MAC:
+                    jpackagePath += "jpackage";
+                    break;
+                default:
+                    Log.error("Cannot determine platform type.");
+                    break;
+            }
+
+            Log.verbose("jpackage: " + jpackagePath);
+        }
+
+        return jpackagePath;
+    }
+
+    public static String getIconFormat(String icon) {
+        // GIF, JPEG, ICO, or PNG
+        if (icon.toLowerCase().endsWith(".gif")) {
+            return "GIF";
+        } else if (icon.toLowerCase().endsWith(".jpg")) {
+            return "JPEG";
+        } else if (icon.toLowerCase().endsWith(".ico")) {
+            return "ICO";
+        } else if (icon.toLowerCase().endsWith(".png")) {
+            return "PNG";
+        }
+
+        return "UNKNOWN";
+    }
+
+    public static boolean isIconSupported(String icon) {
+        Platform platform = Platform.getPlatform();
+        switch (platform) {
+            case WINDOWS:
+                if (icon.endsWith(".ico")) {
+                    return true;
+                } else {
+                    Log.warning("Icon file format (" + getIconFormat(icon) + ") is not supported on Windows for file " + icon + ".");
+                    return false;
+                }
+            case LINUX:
+                if (icon.endsWith(".png")) {
+                    return true;
+                } else {
+                    Log.warning("Icon file format (" + getIconFormat(icon) + ") is not supported on Linux for file " + icon + ".");
+                    return false;
+                }
+            case MAC:
+                Log.warning("Icon file format (" + getIconFormat(icon) + ") is not supported on OS X for file " + icon + ".");
+                return false;
+        }
+
+        return false;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/Log.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
+ * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
+ */
+package jnlp.converter;
+
+public class Log {
+    private static boolean verbose = false;
+
+    public static void setVerbose(boolean verbose) {
+        Log.verbose = verbose;
+    }
+
+    public static boolean isVerbose() {
+        return verbose;
+    }
+
+    public static void verbose(String msg) {
+        if (verbose) {
+            System.out.println(msg);
+        }
+    }
+
+    public static void info(String msg) {
+        System.out.println("Info: " + msg);
+    }
+
+    public static void warning(String msg) {
+        System.err.println("Warning: " + msg);
+    }
+
+    public static void error(String msg) {
+        System.err.println("Error: " + msg);
+        System.exit(1);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/Main.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jnlp.converter;
+
+import java.io.File;
+
+public class Main {
+
+    private static void showHelp() {
+        Options.showHelp();
+    }
+
+    private static void showVersion() {
+        System.out.println("Version: 1.0");
+    }
+
+    private static void createBundle(Options options) {
+        Log.verbose("Creating bundle for JNLP: " + options.getJNLP());
+        Log.verbose("Output folder: " + options.getOutput());
+
+        JNLPConverter converter = new JNLPConverter(options);
+        converter.convert();
+    }
+
+    private static void validateJDK() {
+        String jpackagePath = JNLPConverter.getJPackagePath();
+        File file = new File(jpackagePath);
+        if (!file.exists()) {
+            Log.error("Cannot find " + jpackagePath + ". Make sure you running JNLPConverter with supported JDK version.");
+        }
+    }
+
+    public static void main(String[] args) {
+        Options options = Options.parseArgs(args); // Only valid options will be returned
+
+        Log.setVerbose(options.verbose());
+
+        validateJDK();
+
+        if (options.help()) {
+            showHelp();
+        } else if (options.version()) {
+            showVersion();
+        } else {
+            createBundle(options);
+        }
+
+        System.exit(0);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/Options.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,370 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jnlp.converter;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+public class Options {
+
+    private boolean createImage = false;
+    private boolean createInstaller = false;
+    private String installerType = null;
+    private String jnlp = null;
+    private String output = null;
+    private String keep = null;
+    private boolean help = false;
+    private boolean verbose = false;
+    private boolean version = false;
+    private final List<String> jpackageOptions = new ArrayList<>();
+    private boolean isRuntimeImageSet = false;
+
+    private static final String JNLP_OPTION_PREFIX = "--jnlp=";
+    private static final String OUTPUT_OPTION_PREFIX = "--output=";
+    private static final String KEEP_OPTION_PREFIX = "--keep=";
+    private static final String JNLP_OPTION_SHORT_PREFIX = "-j";
+    private static final String OUTPUT_OPTION_SHORT_PREFIX = "-o";
+    private static final String KEEP_OPTION_SHORT_PREFIX = "-k";
+
+    private static final String [] INSTALLER_TYPES = {"msi", "rpm", "deb",
+                                                      "dmg", "pkg", "pkg-app-store"};
+
+    // --output, -o, --input, -i, --files, -f, --main-jar, -j, --class, -c
+    private static final String [] BLOCKED_JPACKAGE_OPTIONS = {"--output", "-o", "--input", "-i",
+                                                                "--files", "-f", "--main-jar",
+                                                                "-j", "--class", "-c"};
+
+    private static final String RUNTIME_IMAGE_OPTION = "--runtime-image";
+
+    private static final String ERR_UNKNOWN_OPTION = "Unknown option: ";
+    private static final String ERR_MISSING_VALUE = "Value is required for option ";
+    private static final String ERR_MISSING_MODE = "Error: create-image or create-installer mode is required";
+    private static final String ERR_MISSING_JNLP = "Error: --jnlp is required";
+    private static final String ERR_MISSING_OUTPUT = "Error: --output is required";
+    private static final String ERR_OUTPUT_EXISTS = "Error: output folder already exists";
+    private static final String ERR_KEEP_EXISTS = "Error: folder for --keep argument already exists";
+    private static final String ERR_INVALID_PROTOCOL_JNLP = "Error: Invalid protocol for JNLP file. Only HTTP, HTTPS and FILE protocols are supported.";
+
+    public boolean createImage() {
+        return createImage;
+    }
+
+    public boolean createInstaller() {
+        return createInstaller;
+    }
+
+    public String getInstallerType() {
+        return installerType;
+    }
+
+    public String getJNLP() {
+        return jnlp;
+    }
+
+    public String getOutput() {
+        return output;
+    }
+
+    public String keep() {
+        return keep;
+    }
+
+    public boolean help() {
+        return help;
+    }
+
+    public boolean verbose() {
+        return verbose;
+    }
+
+    public boolean version() {
+        return version;
+    }
+
+    public List<String> getJPackageOptions() {
+        return jpackageOptions;
+    }
+
+    public boolean isRuntimeImageSet() {
+        return isRuntimeImageSet;
+    }
+
+    // Helper method to dump all options
+    private void display() {
+        System.out.println("Options:");
+        System.out.println("createImage: " + createImage);
+        System.out.println("createInstaller: " + createInstaller);
+        System.out.println("installerType: " + installerType);
+        System.out.println("jnlp: " + jnlp);
+        System.out.println("output: " + output);
+        System.out.println("keep: " + keep);
+        System.out.println("help: " + help);
+        System.out.println("verbose: " + verbose);
+        System.out.println("version: " + version);
+        for (int i = 0; i < jpackageOptions.size(); i++) {
+            System.out.println("jpackageOptions[" + i + "]: " + jpackageOptions.get(i));
+        }
+    }
+
+    private void validate() {
+        if (help || version) {
+            return;
+        }
+
+        if (!createImage && !createInstaller) {
+            optionError(ERR_MISSING_MODE);
+        }
+
+        if (jnlp == null) {
+            optionError(ERR_MISSING_JNLP);
+        } else {
+            int index = jnlp.indexOf(":");
+            if (index == -1 || index == 0) {
+                optionError(ERR_INVALID_PROTOCOL_JNLP);
+            } else {
+                String protocol = jnlp.substring(0, index);
+                if (!protocol.equalsIgnoreCase("http") &&
+                    !protocol.equalsIgnoreCase("https") &&
+                    !protocol.equalsIgnoreCase("file")) {
+                    optionError(ERR_INVALID_PROTOCOL_JNLP);
+                }
+            }
+        }
+
+        if (output == null) {
+            optionError(ERR_MISSING_OUTPUT);
+        } else {
+            File file = new File(output);
+            if (file.exists()) {
+                optionErrorNoHelp(ERR_OUTPUT_EXISTS);
+            }
+        }
+
+        if (keep != null) {
+            File file = new File(keep);
+            if (file.exists()) {
+                optionErrorNoHelp(ERR_KEEP_EXISTS);
+            }
+        }
+
+        jpackageOptions.forEach((option) -> {
+            if (isBlockedOption(option)) {
+                Log.error(option + " is not allowed via --jpackage-options, since it will conflict with "
+                        + "same option generated by JNLPConverter.");
+            }
+        });
+    }
+
+    public boolean isOptionPresent(String option) {
+        for (String jpackageOption : jpackageOptions) {
+            if (jpackageOption.equalsIgnoreCase(option)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    private boolean isBlockedOption(String option) {
+        for (String blockedOption : BLOCKED_JPACKAGE_OPTIONS) {
+            if (blockedOption.equalsIgnoreCase(option)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    public static void showHelp() {
+//      System.out.println("********* Help should not be longer then 80 characters as per JEP-293 *********");
+        System.out.println("Usage: java -jar JNLPConverter.jar <mode> <options>");
+        System.out.println("");
+        System.out.println("where mode is one of:");
+        System.out.println("  create-image");
+        System.out.println("          Generates a platform-specific application image.");
+        System.out.println("  create-installer <type>");
+        System.out.println("          Generates a platform-specific installer for the application.");
+        System.out.println("          Valid values for \"type\" are \"msi\", \"rpm\", \"deb\", \"dmg\", \"pkg\",");
+        System.out.println("          \"pkg-app-store\". If \"type\" is omitted, all supported types of installable");
+        System.out.println("          packages for current platform will be generated.");
+        System.out.println("");
+        System.out.println("Possible options include:");
+        System.out.println("  -j, --jnlp <path>");
+        System.out.println("          Full path to JNLP file. Supported protocols are HTTP/HTTPS/FILE.");
+        System.out.println("  -o, --output <path>");
+        System.out.println("          Name of the directory where generated output files are placed.");
+        System.out.println("  -k, --keep <path>");
+        System.out.println("          Keep JNLP, JARs and command line arguments for jpackage");
+        System.out.println("          in directory provided.");
+        System.out.println("      --jpackage-options <options>");
+        System.out.println("          Specify additional jpackage options or overwrite provided by JNLPConverter.");
+        System.out.println("          All jpackage options can be specified except: --output -o, --input -i,");
+        System.out.println("          --files -f, --main-jar -j and --class -c.");
+        System.out.println("  -h, --help, -?");
+        System.out.println("          Print this help message");
+        System.out.println("  -v, --verbose");
+        System.out.println("          Enable verbose output.");
+        System.out.println("      --version");
+        System.out.println("          Version information.");
+        System.out.println("To specify an argument for a long option, you can use --<name>=<value> or");
+        System.out.println("--<name> <value>.");
+        System.out.println("To specify proxy server use standard Java properties http.proxyHost and http.proxyPort.");
+    }
+
+    private static boolean isInstallerType(String type) {
+        for (String installerType : INSTALLER_TYPES) {
+            if (installerType.equals(type)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    public static Options parseArgs(String[] args) {
+        Options options = new Options();
+
+        int index = 0;
+        if (args.length >= 1) {
+            switch (args[0]) {
+                case "create-image":
+                    options.createImage = true;
+                    index = 1;
+                    break;
+                case "create-installer":
+                    options.createInstaller = true;
+                    index = 1;
+                    if (args.length >= 2) {
+                        if (isInstallerType(args[1])) {
+                            options.installerType = args[1];
+                            index = 2;
+                        }
+                    }
+                    break;
+                case "-h":
+                case "--help":
+                case "-?":
+                case "--version":
+                    break;
+                default:
+                    optionError(Options.ERR_MISSING_MODE);
+                    break;
+            }
+        }
+
+        for (int i = index; i < args.length; i++) {
+            String arg = args[i];
+
+            if (arg.equals("--jnlp")) {
+                if (++i >= args.length) {
+                    optionError(Options.ERR_MISSING_VALUE, "--jnlp");
+                }
+                options.jnlp = args[i];
+            }  else if (arg.startsWith(JNLP_OPTION_PREFIX)) {
+                options.jnlp = arg.substring(JNLP_OPTION_PREFIX.length());
+            } else if (arg.equals("--output")) {
+                if (++i >= args.length) {
+                    optionError(Options.ERR_MISSING_VALUE, "--output");
+                }
+                options.output = args[i];
+            } else if (arg.startsWith(OUTPUT_OPTION_PREFIX)) {
+                options.output = arg.substring(OUTPUT_OPTION_PREFIX.length());
+            } else if (arg.equals("--keep")) {
+                if (++i >= args.length) {
+                    optionError(Options.ERR_MISSING_VALUE, "--keep");
+                }
+                options.keep = args[i];
+            } else if (arg.startsWith(KEEP_OPTION_PREFIX)) {
+                options.keep = arg.substring(KEEP_OPTION_PREFIX.length());
+            } else if (arg.equals("--help")) {
+                options.help = true;
+            } else if (arg.equals("--verbose")) {
+                options.verbose = true;
+            } else if (arg.equals("--version")) {
+                options.version = true;
+            } else if (arg.equals("-j")) { // short options
+                if (++i >= args.length) {
+                    optionError(Options.ERR_MISSING_VALUE, "-j");
+                }
+                options.jnlp = args[i];
+            } else if (arg.startsWith(JNLP_OPTION_SHORT_PREFIX)) {
+                options.jnlp = arg.substring(JNLP_OPTION_SHORT_PREFIX.length());
+            } else if (arg.equals("-o")) {
+                if (++i >= args.length) {
+                    optionError(Options.ERR_MISSING_VALUE, "-o");
+                }
+                options.output = args[i];
+            } else if (arg.startsWith(OUTPUT_OPTION_SHORT_PREFIX)) {
+                options.output = arg.substring(OUTPUT_OPTION_SHORT_PREFIX.length());
+            } else if (arg.equals("-k")) {
+                if (++i >= args.length) {
+                    optionError(Options.ERR_MISSING_VALUE, "-k");
+                }
+                options.keep = args[i];
+            } else if (arg.startsWith(KEEP_OPTION_SHORT_PREFIX)) {
+                options.keep = arg.substring(KEEP_OPTION_SHORT_PREFIX.length());
+            } else if (arg.equals("-h") || arg.equals("-?")) {
+                options.help = true;
+            } else if (arg.equals("-v")) {
+                options.verbose = true;
+            } else if (arg.equals("--jpackage-options")) {
+                for (i = (i + 1); i < args.length; i++) {
+                    if (!options.isRuntimeImageSet) {
+                        if (args[i].equals(RUNTIME_IMAGE_OPTION)) {
+                            options.isRuntimeImageSet = true;
+                        }
+                    }
+                    options.jpackageOptions.add(args[i]);
+                }
+            } else {
+                optionError(ERR_UNKNOWN_OPTION, arg);
+            }
+        }
+
+        //options.display(); // For testing only
+        options.validate();
+
+        return options;
+    }
+
+    private static void optionErrorNoHelp(String msg) {
+        System.out.println(msg);
+        System.exit(1);
+    }
+
+    private static void optionError(String msg) {
+        System.out.println(msg);
+        System.out.println();
+        showHelp();
+        System.exit(1);
+    }
+
+    private static void optionError(String msg, String option) {
+        System.out.println(msg + option);
+        System.out.println();
+        showHelp();
+        System.exit(1);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/Platform.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jnlp.converter;
+
+import java.util.regex.Pattern;
+
+public enum Platform {
+    UNKNOWN, WINDOWS, LINUX, MAC;
+    private static final Platform platform;
+    private static final int majorVersion;
+    private static final int minorVersion;
+
+    static {
+        String os = System.getProperty("os.name").toLowerCase();
+
+        if (os.contains("win")) {
+            platform = Platform.WINDOWS;
+        } else if (os.contains("nix") || os.contains("nux")) {
+            platform = Platform.LINUX;
+        } else if (os.contains("mac")) {
+            platform = Platform.MAC;
+        } else {
+            platform = Platform.UNKNOWN;
+        }
+
+        String version = System.getProperty("os.version");
+        String[] parts = version.split(Pattern.quote("."));
+
+        if (parts.length > 0) {
+            majorVersion = Integer.parseInt(parts[0]);
+
+            if (parts.length > 1) {
+                minorVersion = Integer.parseInt(parts[0]);
+            } else {
+                minorVersion = -1;
+            }
+        } else {
+            majorVersion = -1;
+            minorVersion = -1;
+        }
+    }
+
+    private Platform() {
+    }
+
+    public static Platform getPlatform() {
+        return platform;
+    }
+
+    public static boolean isWindows() {
+        return (platform == Platform.WINDOWS);
+    }
+
+    public static int getMajorVersion() {
+        return majorVersion;
+    }
+
+    public static int getMinorVersion() {
+        return minorVersion;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/GeneralUtil.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,415 @@
+/*
+ * Copyright (c) 2006, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jnlp.converter.parser;
+
+import java.util.Locale;
+import java.util.ArrayList;
+import java.util.StringTokenizer;
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+/**
+ * Handy class to add some utility methods for dealing with property matching
+ * etc.
+ */
+public class GeneralUtil {
+
+    public static boolean prefixMatchStringList(String[] prefixList, String target) {
+        // No prefixes matches everything
+        if (prefixList == null) {
+            return true;
+        }
+        // No target, but a prefix list does not match anything
+        if (target == null) {
+            return false;
+        }
+        for (String prefix : prefixList) {
+            if (target.startsWith(prefix)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static String getOSArch() {
+        return System.getProperty("os.arch");
+    }
+
+    public static boolean prefixMatchArch(String[] prefixList) {
+        // No prefixes matches everything
+        if (prefixList == null) {
+            return true;
+        }
+
+        // check for the current arch
+        String arch = getOSArch();
+        for (String prefix : prefixList) {
+            if (arch.startsWith(prefix)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Converts a space delimited string to a list of strings
+     */
+    public static String[] getStringList(String str) {
+        if (str == null) {
+            return null;
+        }
+        ArrayList<String> list = new ArrayList<>();
+        int i = 0;
+        int length = str.length();
+        StringBuffer sb = null;
+        while (i < length) {
+            char ch = str.charAt(i);
+            switch (ch) {
+                case ' ':
+                    // A space was hit. Add string to list
+                    if (sb != null) {
+                        list.add(sb.toString());
+                        sb = null;
+                    }
+                    break;
+                case '\\':
+                    // It is a delimiter. Add next character
+                    if (i + 1 < length) {
+                        ch = str.charAt(++i);
+                        if (sb == null) {
+                            sb = new StringBuffer();
+                        }
+                        sb.append(ch);
+                    }
+                    break;
+                default:
+                    if (sb == null) {
+                        sb = new StringBuffer();
+                    }   sb.append(ch);
+                    break;
+            }
+            i++; // Next character
+        }
+        // Make sure to add the last part to the list too
+        if (sb != null) {
+            list.add(sb.toString());
+        }
+        if (list.isEmpty()) {
+            return null;
+        }
+        String[] results = new String[list.size()];
+        return list.toArray(results);
+    }
+
+    /**
+     * Checks if string list matches default locale
+     */
+    public static boolean matchLocale(String[] localeList, Locale locale) {
+        // No locale specified matches everything
+        if (localeList == null) {
+            return true;
+        }
+        for (String localeList1 : localeList) {
+            if (matchLocale(localeList1, locale)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Checks if string matches default locale
+     */
+    public static boolean matchLocale(String localeStr, Locale locale) {
+        if (localeStr == null || localeStr.length() == 0) {
+            return true;
+        }
+
+        // Compare against default locale
+        String language;
+        String country;
+        String variant;
+
+        // The locale string is of the form language_country_variant
+        StringTokenizer st = new StringTokenizer(localeStr, "_", false);
+        if (st.hasMoreElements() && locale.getLanguage().length() > 0) {
+            language = st.nextToken();
+            if (!language.equalsIgnoreCase(locale.getLanguage())) {
+                return false;
+            }
+        }
+        if (st.hasMoreElements() && locale.getCountry().length() > 0) {
+            country = st.nextToken();
+            if (!country.equalsIgnoreCase(locale.getCountry())) {
+                return false;
+            }
+        }
+        if (st.hasMoreElements() && locale.getVariant().length() > 0) {
+            variant = st.nextToken();
+            if (!variant.equalsIgnoreCase(locale.getVariant())) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    public static long heapValToLong(String heapValue) {
+        if (heapValue == null) {
+            return -1;
+        }
+        long multiplier = 1;
+        if (heapValue.toLowerCase().lastIndexOf('m') != -1) {
+            // units are megabytes, 1 megabyte = 1024 * 1024 bytes
+            multiplier = 1024 * 1024;
+            heapValue = heapValue.substring(0, heapValue.length() - 1);
+        } else if (heapValue.toLowerCase().lastIndexOf('k') != -1) {
+            // units are kilobytes, 1 kilobyte = 1024 bytes
+            multiplier = 1024;
+            heapValue = heapValue.substring(0, heapValue.length() - 1);
+        }
+        long theValue;
+        try {
+            theValue = Long.parseLong(heapValue);
+            theValue = theValue * multiplier;
+        } catch (NumberFormatException e) {
+            theValue = -1;
+        }
+        return theValue;
+    }
+
+    public static byte[] readBytes(InputStream is, long size) throws IOException {
+        // Sanity on file size (restrict to 1M)
+        if (size > 1024 * 1024) {
+            throw new IOException("File too large");
+        }
+
+        BufferedInputStream bis;
+        if (is instanceof BufferedInputStream) {
+            bis = (BufferedInputStream) is;
+        } else {
+            bis = new BufferedInputStream(is);
+        }
+
+        if (size <= 0) {
+            size = 10 * 1024; // Default to 10K
+        }
+        byte[] b = new byte[(int) size];
+        int n;
+        int bytesRead = 0;
+        n = bis.read(b, bytesRead, b.length - bytesRead);
+        while (n != -1) {
+            bytesRead += n;
+            // Still room in array
+            if (b.length == bytesRead) {
+                byte[] bb = new byte[b.length * 2];
+                System.arraycopy(b, 0, bb, 0, b.length);
+                b = bb;
+            }
+            // Read next line
+            n = bis.read(b, bytesRead, b.length - bytesRead);
+        }
+        bis.close();
+        is.close();
+
+        if (bytesRead != b.length) {
+            byte[] bb = new byte[bytesRead];
+            System.arraycopy(b, 0, bb, 0, bytesRead);
+            b = bb;
+        }
+        return b;
+    }
+
+    public static String getOSFullName() {
+        return System.getProperty("os.name");
+    }
+
+    /**
+     * Makes sure a URL is a path URL, i.e., ends with '/'
+     */
+    public static URL asPathURL(URL url) {
+        if (url == null) {
+            return null;
+        }
+
+        String path = url.getFile();
+        if (path != null && !path.endsWith("/")) {
+            try {
+                return new URL(url.getProtocol(),
+                        url.getHost(),
+                        url.getPort(),
+                        url.getFile() + "/");
+            } catch (MalformedURLException mue) {
+                // Should not happen
+            }
+        }
+        // Just return same URl
+        return url;
+    }
+
+    public static Locale getDefaultLocale() {
+        return Locale.getDefault();
+    }
+
+    public static String toNormalizedString(URL u) {
+        if (u == null) {
+            return "";
+        }
+
+        try {
+            if (u.getPort() == u.getDefaultPort()) {
+                u = new URL(u.getProtocol().toLowerCase(),
+                        u.getHost().toLowerCase(), -1, u.getFile());
+            } else {
+                u = new URL(u.getProtocol().toLowerCase(),
+                        u.getHost().toLowerCase(), u.getPort(), u.getFile());
+            }
+        } catch (MalformedURLException ex) {
+        }
+        return u.toExternalForm();
+    }
+
+    public static boolean sameURLs(URL u1, URL u2) {
+        if (u1 == null || u2 == null || (u1 == u2)) {
+            return (u1 == u2);
+        }
+        //NB: do not use URL.sameFile() as it will do DNS lookup
+        // Also, do quick check before slow string comparisons
+        String f1 = u1.getFile();
+        String f2 = u2.getFile();
+        return (f1.length() == f2.length()) && sameBase(u1, u2)
+                && f1.equalsIgnoreCase(f2);
+    }
+
+    public static boolean sameBase(URL u1, URL u2) {
+        return u1 != null && u2 != null &&
+                sameHost(u1, u2) && samePort(u1, u2) && sameProtocol(u1, u2);
+    }
+
+    private static boolean sameProtocol(URL u1, URL u2) {
+        //protocols are known to be lowercase
+        return u1.getProtocol().equals(u2.getProtocol());
+    }
+
+    private static boolean sameHost(URL u1, URL u2) {
+        String host = u1.getHost();
+        String otherHost = u2.getHost();
+        if (host == null || otherHost == null) {
+            return (host == null && otherHost == null);
+        } else {
+            //avoid slow comparison for strings of different length
+            return ((host.length() == otherHost.length())
+                       && host.equalsIgnoreCase(otherHost));
+        }
+    }
+
+    private static boolean samePort(URL u1, URL u2) {
+        return getPort(u1) == getPort(u2);
+    }
+
+    public static int getPort(URL u) {
+        if (u.getPort() != -1) {
+            return u.getPort();
+        } else {
+            return u.getDefaultPort();
+        }
+    }
+
+    public static URL getBase(URL url) {
+        if (url == null) return null;
+        String file = url.getFile();
+        if (file != null) {
+            int idx = file.lastIndexOf('/');
+            if (idx != -1 ) {
+                file = file.substring(0, idx + 1);
+            }
+            try {
+                return new URL(
+                    url.getProtocol(),
+                    url.getHost(),
+                    url.getPort(),
+                    file);
+            } catch(MalformedURLException mue) {
+                System.err.println(mue.getMessage());
+            }
+        }
+        // Just return same URL
+        return url;
+    }
+
+    private static String getEmbeddedVersionPath(String path, String version) {
+        int index = path.lastIndexOf("/");
+        String filename = path.substring(index + 1);
+        path = path.substring(0, index + 1);
+
+        String ext = null;
+        index = filename.lastIndexOf(".");
+        if (index != -1) {
+            ext = filename.substring(index + 1);
+            filename = filename.substring(0, index);
+        }
+
+        StringBuilder filenameSB = new StringBuilder(filename);
+        if (version != null) {
+            filenameSB.append("__V");
+            filenameSB.append(version);
+        }
+        if (ext != null) {
+            filenameSB.append(".");
+            filenameSB.append(ext);
+        }
+
+        path += filenameSB.toString();
+        return path;
+    }
+
+    public static URL getEmbeddedVersionURL(URL u, String version) throws Exception {
+        if (u == null) {
+            return null;
+        }
+
+        if (version == null || version.indexOf("*") != -1
+                || version.indexOf("+") != -1) {
+            // Do not support * or + in version string
+            return u;
+        }
+
+        URL versionURL = null;
+
+        String protocol = u.getProtocol();
+        String host = u.getHost();
+        int port = u.getPort();
+        String path = u.getPath();
+
+        path = getEmbeddedVersionPath(path, version);
+
+        versionURL = new URL(protocol, host, port, path);
+
+        return versionURL;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/JNLPDesc.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,617 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jnlp.converter.parser;
+
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Properties;
+import jnlp.converter.Log;
+
+import jnlp.converter.parser.ResourcesDesc.JARDesc;
+import jnlp.converter.parser.ResourcesDesc.JREDesc;
+
+public class JNLPDesc {
+    private String specVersion = null;
+    private String codebase = null;
+    private String version = null;
+    private String href = null;
+    private String name = null;
+    private String title = null;
+    private String vendor = null;
+    private String mainJar = null;
+    private String [] descriptions = null;
+    private IconDesc [] icons = null;
+    private ShortcutDesc shortcuts = null;
+    private AssociationDesc [] associations = null;
+    private String mainClass = null;
+    private final List<String> arguments  = new ArrayList<>();
+    private final List<String> files = new ArrayList<>();
+    private final List<JARDesc> resources = new ArrayList<>();
+    private final List<String> vmArgs = new ArrayList<>();
+    private boolean isLibrary = false;
+    private boolean isInstaller = false;
+    private boolean isJRESet = false;
+    private ResourcesDesc resourcesDesc;
+    private boolean isVersionEnabled = false;
+    private boolean isSandbox = true;
+    private boolean isFXApp = false;
+
+    public void setSpecVersion(String specVersion) {
+        this.specVersion = specVersion;
+
+        // Valid values are 1.0, 1.5, 6.0, 6.0.10, 6.0.18, 7.0, 8.20, 9 or a wildcard such as 1.0+.
+        if (!specVersion.startsWith("1.0") &&
+            !specVersion.startsWith("1.5") &&
+            !specVersion.startsWith("6.0") &&
+            !specVersion.startsWith("6.0.10") &&
+            !specVersion.startsWith("6.0.18") &&
+            !specVersion.startsWith("7.0") &&
+            !specVersion.startsWith("8.20") &&
+            !specVersion.startsWith("9")) {
+                System.out.println("Warning: Invalid version of the JNLP specification found: "
+                        + specVersion + ". Valid values are 1.0, 1.5, 6.0, 6.0.10, 6.0.18, 7.0,"
+                        + " 8.20, 9 or a wildcard such as 1.0+.");
+        }
+    }
+
+    public String getSpecVersion() {
+        return specVersion;
+    }
+
+    public void setCodebase(String codebase) {
+        this.codebase = codebase;
+    }
+
+    public String getCodebase() {
+        return codebase;
+    }
+
+    public void setVersion(String version) {
+        this.version = version;
+    }
+
+    public String getVersion() {
+        return version;
+    }
+
+    public void setHref(String href) {
+        this.href = href;
+    }
+
+    public String getHref() {
+        return href;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setVendor(String vendor) {
+        this.vendor = vendor;
+    }
+
+    public String getVendor() {
+        return vendor;
+    }
+
+    public void setMainJar(String mainJar) {
+        if (this.mainJar == null) {
+            this.mainJar = mainJar;
+        } else {
+            Log.warning("Main jar already set to '" + this.mainJar + "'. "
+                    + "Attempt to set main jar to '" + mainJar + "' will be ignored.");
+        }
+    }
+
+    public String getMainJar() {
+        return mainJar;
+    }
+
+    public void setDescriptions(String [] descriptions) {
+        this.descriptions = descriptions;
+    }
+
+    public String getDescription() {
+        String description = null;
+        if (descriptions != null) {
+            if (descriptions[InformationDesc.DESC_DEFAULT] != null) {
+                description = descriptions[InformationDesc.DESC_DEFAULT];
+            } else if (descriptions[InformationDesc.DESC_SHORT] != null) {
+                description = descriptions[InformationDesc.DESC_SHORT];
+            } else if (descriptions[InformationDesc.DESC_ONELINE] != null) {
+                description = descriptions[InformationDesc.DESC_ONELINE];
+            } else if (descriptions[InformationDesc.DESC_TOOLTIP] != null) {
+                description = descriptions[InformationDesc.DESC_TOOLTIP];
+            }
+
+            if (description != null) {
+                if (description.contains("\r") || description.contains("\n")) {
+                    Log.warning("Multiple lines of text in description is not supported and description will be converted to single line by replacing new lines with spaces.");
+                    Log.warning("Original description:");
+                    Log.warning(description);
+                    String descs[] = description.split("\n");
+                    description = "";
+                    for (String desc : descs) {
+                        desc = desc.trim();
+                        if (desc.endsWith("\r")) { // In case new line was \r\n
+                            if (desc.length() != 1) {
+                                desc = desc.substring(0, desc.length() - 1);
+                            } else {
+                                continue;
+                            }
+                        }
+
+                        if (desc.isEmpty()) {
+                            continue;
+                        }
+
+                        if (!description.isEmpty()) {
+                            description += " ";
+                        }
+
+                        description += desc;
+                    }
+                    Log.warning("Converted description:");
+                    Log.warning(description);
+                }
+            }
+        }
+
+        return description;
+    }
+
+    public void setIcons(IconDesc [] icons) {
+        this.icons = icons;
+    }
+
+    public IconDesc getIcon() {
+        for (IconDesc icon : icons) {
+            if (icon.getKind() == IconDesc.ICON_KIND_DEFAULT) {
+                return icon;
+            }
+        }
+
+        for (IconDesc icon : icons) {
+            if (icon.getKind() == IconDesc.ICON_KIND_SHORTCUT) {
+                return icon;
+            }
+        }
+
+        return null;
+    }
+
+    public String getIconLocation() {
+        IconDesc icon = getIcon();
+        if (icon != null) {
+            return icon.getLocalLocation();
+        }
+
+        return null;
+    }
+
+    public void setShortcuts(ShortcutDesc shortcuts) {
+        this.shortcuts = shortcuts;
+    }
+
+    public boolean isDesktopHint() {
+        if (shortcuts != null) {
+            return shortcuts.getDesktop();
+        }
+
+        return false;
+    }
+
+    public boolean isMenuHint() {
+        if (shortcuts != null) {
+            return shortcuts.getMenu();
+        }
+
+        return false;
+    }
+
+    public String getSubMenu() {
+        if (shortcuts != null) {
+            return shortcuts.getSubmenu();
+        }
+
+        return null;
+    }
+
+    public void setAssociations(AssociationDesc [] associations) {
+        this.associations = associations;
+    }
+
+    public AssociationDesc [] getAssociations() {
+         return associations;
+    }
+
+    public void setMainClass(String mainClass, boolean isJavafxDesc) {
+        if (isJavafxDesc) {
+            this.mainClass = mainClass;
+        } else if (this.mainClass == null) {
+            this.mainClass = mainClass;
+        }
+    }
+
+    public String getMainClass() {
+        return mainClass;
+    }
+
+    public void addArguments(String argument) {
+        if (argument != null && !argument.isEmpty()) {
+            arguments.add(argument);
+        }
+    }
+
+    public List<String> getArguments() {
+        return arguments;
+    }
+
+    public void setProperty(String name, String value) {
+        if (name.equalsIgnoreCase("jnlp.versionEnabled") && value.equalsIgnoreCase("true")) {
+            isVersionEnabled = true;
+            return;
+        }
+
+        addVMArg("-D" + name + "=" + value);
+    }
+
+    public boolean isVersionEnabled() {
+        return isVersionEnabled;
+    }
+
+    public boolean isSandbox() {
+        return isSandbox;
+    }
+
+    public void setIsSandbox(boolean value) {
+        isSandbox = value;
+    }
+
+    public boolean isFXApp() {
+        return isFXApp;
+    }
+
+    public void setIsFXApp(boolean value) {
+        isFXApp = value;
+    }
+
+    public void addFile(String file) {
+        if (file != null) {
+            files.add(file);
+        }
+    }
+
+    public List<String> getFiles() {
+        return files;
+    }
+
+    private boolean isResourceExists(JARDesc resource) {
+        for (JARDesc r : resources) {
+            if (r.getLocation().equals(resource.getLocation())) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    public void addResource(JARDesc resource) {
+        if (resource != null) {
+            if (isResourceExists(resource)) {
+                Log.warning("Ignoring repeated resource " + resource.getLocation());
+                return;
+            }
+            resources.add(resource);
+        }
+    }
+
+    public List<JARDesc> getResources() {
+        return resources;
+    }
+
+    public void addVMArg(String arg) {
+        if (arg != null) {
+            vmArgs.add(arg);
+        }
+    }
+
+    public List<String> getVMArgs() {
+        return vmArgs;
+    }
+
+    public void setIsLibrary(boolean isLibrary) {
+        this.isLibrary = isLibrary;
+    }
+
+    public boolean isLibrary() {
+        return isLibrary;
+    }
+
+    public void setIsInstaller(boolean isInstaller) {
+        this.isInstaller = isInstaller;
+    }
+
+    public boolean isInstaller() {
+        return isInstaller;
+    }
+
+    public void setIsJRESet(boolean isJRESet) {
+        this.isJRESet = isJRESet;
+    }
+
+    public boolean isJRESet() {
+        return isJRESet;
+    }
+
+    public void setResourcesDesc(ResourcesDesc resourcesDesc) {
+        this.resourcesDesc = resourcesDesc;
+    }
+
+    public ResourcesDesc getResourcesDesc() {
+        return resourcesDesc;
+    }
+
+    public void parseResourceDesc() throws Exception {
+        if (resourcesDesc != null && !resourcesDesc.isEmpty()) {
+            setMainJar(resourcesDesc.getMainJar().getName());
+
+            JARDesc[] jars = resourcesDesc.getAllJarDescs();
+            for (JARDesc jar : jars) {
+                addResource(jar);
+            }
+
+            JREDesc jreDesc = resourcesDesc.getJreDesc();
+            if (jreDesc != null) {
+                String [] args = jreDesc.getVmArgsList();
+                if (args != null) {
+                    for (String arg : args) {
+                        addVMArg(arg);
+                    }
+                }
+
+                if (jreDesc.getMinHeap() != -1) {
+                    addVMArg("-Xms" + jreDesc.getMinHeap());
+                }
+
+                if (jreDesc.getMaxHeap() != -1) {
+                    addVMArg("-Xmx" + jreDesc.getMaxHeap());
+                }
+            }
+
+            Properties props = resourcesDesc.getResourceProperties();
+            Enumeration e = props.propertyNames();
+            while (e.hasMoreElements()) {
+                String key = (String) e.nextElement();
+                String value = props.getProperty(key);
+                setProperty(key, value);
+            }
+        }
+    }
+
+    public static class InformationDesc {
+
+        private final String _title;
+        private final String _vendor;
+        private final String[] _descriptions;
+        private final IconDesc[] _icons;
+        private ShortcutDesc _shortcutHints;
+        private AssociationDesc[] _associations;
+
+        public InformationDesc(String title, String vendor,
+                String[] descriptions,
+                IconDesc[] icons,
+                ShortcutDesc shortcutHints,
+                AssociationDesc[] associations) {
+            _title = (title == null) ? "" : title;
+            _vendor = (vendor == null) ? "" : vendor;
+            if (descriptions == null) {
+                descriptions = new String[NOF_DESC];
+            }
+            _descriptions = descriptions;
+            _icons = icons;
+            _shortcutHints = shortcutHints;
+            _associations = associations;
+        }
+
+        /**
+         * Constants for the getInfoDescription
+         */
+        final public static int DESC_DEFAULT = 0;
+        final public static int DESC_SHORT = 1;
+        final public static int DESC_ONELINE = 2;
+        final public static int DESC_TOOLTIP = 3;
+        final public static int NOF_DESC = 4;
+
+        /**
+         * Information
+         */
+        public String getTitle() {
+            return _title;
+        }
+
+        public String getVendor() {
+            return _vendor;
+        }
+
+        public IconDesc[] getIcons() {
+            return _icons;
+        }
+
+        public ShortcutDesc getShortcut() {
+            if (_shortcutHints == null) {
+                return null;
+            }
+            return new ShortcutDesc(_shortcutHints.getDesktop(), _shortcutHints.getMenu(), _shortcutHints.getSubmenu());
+        }
+
+        public AssociationDesc[] getAssociations() {
+            return _associations;
+        }
+
+        /**
+         * Sets new shortcut hints.
+         *
+         * @param shortcutDesc the new shortcut hints to set
+         */
+        public void setShortcut(ShortcutDesc shortcut) {
+            _shortcutHints = shortcut;
+        }
+
+        /**
+         * Sets new associations.
+         *
+         * @param assoc the association to set
+         */
+        public void setAssociation(AssociationDesc assoc) {
+            if (assoc == null) {
+                _associations = null;
+            } else {
+                _associations = new AssociationDesc[]{assoc};
+            }
+        }
+
+        /**
+         * Returns the description of the given kind. will return null if none
+         * there
+         */
+        public String getDescription(int kind) {
+            return _descriptions[kind];
+        }
+
+        public String[] getDescription() {
+            return _descriptions;
+        }
+    }
+
+    public static class IconDesc {
+
+        private final String _location;
+        private String _localLocation;
+        private final int _kind;
+
+        final public static int ICON_KIND_DEFAULT = 0;
+        final public static int ICON_KIND_SHORTCUT = 5;
+
+        public IconDesc(URL location, int kind) {
+            _location = location.toExternalForm();
+            _kind = kind;
+        }
+
+        public String getLocation() {
+            return _location;
+        }
+
+        public void setLocalLocation(String localLocation) {
+            _localLocation = localLocation;
+        }
+
+        public String getLocalLocation() {
+            return _localLocation;
+        }
+
+        public int getKind() {
+            return _kind;
+        }
+    }
+
+    public static class ShortcutDesc {
+
+        private final boolean _desktop;
+        private final boolean _menu;
+        private final String _submenu;
+
+        public ShortcutDesc(boolean desktop, boolean menu, String submenu) {
+            _desktop = desktop;
+            _menu = menu;
+            _submenu = submenu;
+        }
+
+        public boolean getDesktop() {
+            return _desktop;
+        }
+
+        public boolean getMenu() {
+            return _menu;
+        }
+
+        public String getSubmenu() {
+            return _submenu;
+        }
+    }
+
+    public static class AssociationDesc {
+
+        private final String _extensions;
+        private final String _mimeType;
+        private final String _description;
+        private final String _icon;
+        private String _iconLocalLocation;
+
+        public AssociationDesc(String extensions, String mimeType, String description, URL icon) {
+            _extensions = extensions;
+            _mimeType = mimeType;
+            _description = description;
+            _icon = (icon != null) ? icon.toExternalForm() : null;
+        }
+
+        public void setIconLocalLocation(String localLocation) {
+            _iconLocalLocation = localLocation;
+        }
+
+        public String getIconLocalLocation() {
+            return _iconLocalLocation;
+        }
+
+        public String getExtensions() {
+            return _extensions;
+        }
+
+        public String getMimeType() {
+            return _mimeType;
+        }
+
+        public String getMimeDescription() {
+            return _description;
+        }
+
+        public String getIconUrl() {
+            return _icon;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/ResourceType.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2006, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jnlp.converter.parser;
+
+/*
+ * Public super class for all resource entries
+ */
+interface ResourceType {
+    /** Visit this specific type */
+    void visit(ResourceVisitor visitor) throws Exception;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/ResourceVisitor.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2006, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jnlp.converter.parser;
+
+import jnlp.converter.parser.ResourcesDesc.JARDesc;
+import jnlp.converter.parser.ResourcesDesc.PropertyDesc;
+import jnlp.converter.parser.ResourcesDesc.JREDesc;
+import jnlp.converter.parser.ResourcesDesc.ExtensionDesc;
+
+/**
+ * A visitor class for the various ResourceType objects
+ * with dummy visit methods for all types.
+ */
+public class ResourceVisitor {
+
+    public void visitJARDesc(JARDesc jad) {
+    }
+
+    public void visitPropertyDesc(PropertyDesc prd) {
+    }
+
+    public void visitExtensionDesc(ExtensionDesc ed) throws Exception {
+    }
+
+    public void visitJREDesc(JREDesc jrd) {
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/ResourcesDesc.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,665 @@
+/*
+ * Copyright (c) 2006, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jnlp.converter.parser;
+
+import java.net.URL;
+import java.util.Properties;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Enumeration;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+import jnlp.converter.HTTPHelper;
+
+/**
+ * This class contains information about the codebase and properties, i.e., how
+ * to locate the classes and optional-packages
+ */
+public class ResourcesDesc implements ResourceType {
+
+    private final List<ResourceType> _list;
+    private volatile JNLPDesc _parent = null;
+
+    /**
+     * Create empty resource list
+     */
+    public ResourcesDesc() {
+        _list = new CopyOnWriteArrayList<>();
+    }
+
+    public JNLPDesc getParent() {
+        return _parent;
+    }
+
+    void setParent(JNLPDesc parent) {
+        _parent = parent;
+        for (int i = 0; i < _list.size(); i++) {
+            Object o = _list.get(i);
+            if (o instanceof JREDesc) {
+                JREDesc jredesc = (JREDesc) o;
+                if (jredesc.getNestedResources() != null) {
+                    jredesc.getNestedResources().setParent(parent);
+                }
+            }
+        }
+    }
+
+    public void addResource(ResourceType rd) {
+        if (rd != null) {
+            _list.add(rd);
+        }
+    }
+
+    boolean isEmpty() {
+        return _list.isEmpty();
+    }
+
+    public JARDesc[] getLocalJarDescs() {
+        ArrayList<JARDesc> jds = new ArrayList<>(_list.size());
+        for (ResourceType rt : _list) {
+            if (rt instanceof JARDesc) {
+                jds.add((JARDesc) rt);
+            }
+        }
+        return jds.toArray(new JARDesc[jds.size()]);
+    }
+
+    public JREDesc getJreDesc() {
+        for (ResourceType rt : _list) {
+            if (rt instanceof JREDesc) {
+                return (JREDesc)rt;
+            }
+        }
+
+        return null;
+    }
+
+    public ExtensionDesc[] getExtensionDescs() throws Exception {
+        final ArrayList<ExtensionDesc> extList = new ArrayList<>();
+        visit(new ResourceVisitor() {
+            @Override
+            public void visitExtensionDesc(ExtensionDesc ed) throws Exception {
+              // add all extensiondesc recursively
+                addExtToList(extList);
+            }
+        });
+        return extList.toArray(new ExtensionDesc[extList.size()]);
+    }
+
+    public JARDesc[] getAllJarDescs() throws Exception {
+        List<JARDesc> jarList = new ArrayList<>();
+        addJarsToList(jarList);
+        return jarList.toArray(new JARDesc[jarList.size()]);
+    }
+
+    /**
+     * Add to a list of all the ExtensionDesc. This method goes recusivly through
+     * all ExtensionDesc
+     */
+    private void addExtToList(final List<ExtensionDesc> list) throws Exception {
+        // Iterate through list an add ext jnlp to the list.
+        visit(new ResourceVisitor() {
+            @Override
+            public void visitExtensionDesc(ExtensionDesc ed) throws Exception {
+                if (ed.getExtensionDesc() != null) {
+                    ed.getExtensionDesc().getMainJar();
+                    ResourcesDesc rd = ed.getExtensionDesc().getResourcesDesc();
+                    if (rd != null) {
+                        rd.addExtToList(list);
+                    }
+                }
+                list.add(ed);
+            }
+        });
+    }
+
+    private void addJarsToList(final List<JARDesc> list) throws Exception {
+
+        // Iterate through list an add resources to the list.
+        // The ordering of resources are preserved
+        visit(new ResourceVisitor() {
+            @Override
+            public void visitJARDesc(JARDesc jd) {
+                list.add(jd);
+            }
+
+            @Override
+            public void visitExtensionDesc(ExtensionDesc ed) throws Exception {
+                if (ed.getExtensionDesc() != null) {
+                    ResourcesDesc rd = ed.getExtensionDesc().getResourcesDesc();
+                    if (rd != null) {
+                        rd.addJarsToList(list);
+                    }
+                }
+            }
+        });
+    }
+
+    /**
+     * Get all the resources needed when a specific resource is requested.
+     * Returns null if no resource was found
+     */
+    public JARDesc[] getResource(final URL location) throws Exception {
+        final JARDesc[] resources = new JARDesc[1];
+        // Find the given resource
+        visit(new ResourceVisitor() {
+            @Override
+            public void visitJARDesc(JARDesc jd) {
+                if (GeneralUtil.sameURLs(jd.getLocation(), location)) {
+                    resources[0] = jd;
+                }
+            }
+        });
+
+        // Found no resource?
+        if (resources[0] == null) {
+            return null;
+        }
+
+        // No part, so just one resource
+        return resources;
+    }
+
+    /* Returns the Expected Main Jar
+     *    first jar with attribute main="true"
+     *    else first jar if none has that attribute
+     *    will look in extensions, and nested resource blocks if matching
+     */
+    protected JARDesc getMainJar() throws Exception {
+        // Normal trick to get around final arguments to inner classes
+        final JARDesc[] results = new JARDesc[2];
+
+        visit(new ResourceVisitor() {
+            @Override
+            public void visitJARDesc(JARDesc jd) {
+                if (jd.isJavaFile()) {
+                    // Keep track of first Java File
+                    if (results[0] == null || results[0].isNativeLib()) {
+                        results[0] = jd;
+                    }
+                    // Keep tack of Java File marked main
+                    if (jd.isMainJarFile()) {
+                        results[1] = jd;
+                    }
+                } else if (jd.isNativeLib()) {
+                    // if jnlp extension has only native lib
+                    if (results[0] == null) {
+                        results[0] = jd;
+                    }
+                }
+            }
+
+            @Override
+            public void visitExtensionDesc(ExtensionDesc ed) throws Exception {
+            // only check if no main yet and it is not an installer
+                if (results[1] == null && !ed.isInstaller()) {
+                    JNLPDesc extLd = ed.getExtensionDesc();
+                    if (extLd != null && extLd.isLibrary()) {
+                        ResourcesDesc rd = extLd.getResourcesDesc();
+                        if (rd != null) {
+                          // look for main jar in extension resource
+                            rd.visit(this);
+                        }
+                    }
+                }
+            }
+        });
+
+        // Default is the first, if none is specified as main. This might
+        // return NULL if there is no JAR resources.
+        JARDesc first = results[0];
+        JARDesc main = results[1];
+
+        // if main is null then return first;
+        // libraries have no such thing as a main jar, so return first;
+        // otherwise return main
+        // only returns null if there are no jars.
+        return (main == null) ? first : main;
+    }
+
+    /*
+     *  Get the properties defined for this object
+     */
+    public Properties getResourceProperties() throws Exception {
+        final Properties props = new Properties();
+        visit(new ResourceVisitor() {
+            @Override
+            public void visitPropertyDesc(PropertyDesc pd) {
+                props.setProperty(pd.getKey(), pd.getValue());
+            }
+
+            @Override
+            public void visitExtensionDesc(ExtensionDesc ed) throws Exception {
+                JNLPDesc jnlpd = ed.getExtensionDesc();
+                ResourcesDesc rd = jnlpd.getResourcesDesc();
+                if (rd != null) {
+                    Properties extProps = rd.getResourceProperties();
+                    Enumeration e = extProps.propertyNames();
+                    while (e.hasMoreElements()) {
+                        String key = (String) e.nextElement();
+                        String value = extProps.getProperty(key);
+                        props.setProperty(key, value);
+                    }
+                }
+            }
+        });
+        return props;
+    }
+
+    /*
+     *  Get the properties defined for this object, in the right order.
+     */
+    public List<Property> getResourcePropertyList() throws Exception {
+        final LinkedList<Property> propList = new LinkedList<>();
+        visit(new ResourceVisitor() {
+            @Override
+            public void visitPropertyDesc(PropertyDesc pd) {
+                propList.add(new Property(pd.getKey(), pd.getValue()));
+            }
+        });
+        return propList;
+    }
+
+    /**
+     * visitor dispatch
+     */
+    @Override
+    public void visit(ResourceVisitor rv) throws Exception {
+        for (int i = 0; i < _list.size(); i++) {
+            ResourceType rt = _list.get(i);
+            rt.visit(rv);
+        }
+    }
+
+    public void addNested(ResourcesDesc nested) throws Exception {
+        if (nested != null) {
+            nested.visit(new ResourceVisitor() {
+                @Override
+                public void visitJARDesc(JARDesc jd) {
+                    _list.add(jd);
+                }
+
+                @Override
+                public void visitPropertyDesc(PropertyDesc pd) {
+                    _list.add(pd);
+                }
+
+                @Override
+                public void visitExtensionDesc(ExtensionDesc ed) {
+                    _list.add(ed);
+                }
+            });
+        }
+
+    }
+
+    public static class JARDesc implements ResourceType {
+
+        private URL _location;
+        private String _locationString;
+        private String _version;
+        private boolean _isNativeLib;
+        private boolean _isMainFile;  // Only used for Java JAR files (a main JAR file is implicitly eager)
+        private ResourcesDesc _parent;   // Back-pointer to the Resources that contains this JAR
+
+        public JARDesc(URL location, String version, boolean isMainFile, boolean isNativeLib, ResourcesDesc parent) {
+            _location = location;
+            _locationString = GeneralUtil.toNormalizedString(location);
+            _version = version;
+            _isMainFile = isMainFile;
+            _isNativeLib = isNativeLib;
+            _parent = parent;
+        }
+
+        /**
+         * Type of JAR resource
+         */
+        public boolean isNativeLib() {
+            return _isNativeLib;
+        }
+
+        public boolean isJavaFile() {
+            return !_isNativeLib;
+        }
+
+        /**
+         * Returns URL/version for JAR file
+         */
+        public URL getVersionLocation() throws Exception {
+            if (getVersion() == null) {
+                return _location;
+            } else {
+                return GeneralUtil.getEmbeddedVersionURL(getLocation(), getVersion());
+            }
+        }
+
+        public URL getLocation() {
+            return _location;
+        }
+
+        public String getVersion() {
+            return _version;
+        }
+
+        public String getName() {
+            // File can be separated by '/' or '\\'
+            int index;
+            int index1 = _locationString.lastIndexOf('/');
+            int index2 = _locationString.lastIndexOf('\\');
+
+            if (index1 >= index2) {
+                index = index1;
+            } else {
+                index = index2;
+            }
+
+            if (index != -1) {
+                return _locationString.substring(index + 1, _locationString.length());
+            }
+
+            return null;
+        }
+
+        /**
+         * Returns if this is the main JAR file
+         */
+        public boolean isMainJarFile() {
+            return _isMainFile;
+        }
+
+        /**
+         * Get parent LaunchDesc
+         */
+        public ResourcesDesc getParent() {
+            return _parent;
+        }
+
+        /**
+         * Visitor dispatch
+         */
+        public void visit(ResourceVisitor rv) {
+            rv.visitJARDesc(this);
+        }
+    }
+
+    public static class PropertyDesc implements ResourceType {
+
+        private String _key;
+        private String _value;
+
+        public PropertyDesc(String key, String value) {
+            _key = key;
+            _value = value;
+        }
+
+        // Accessors
+        public String getKey() {
+            return _key;
+        }
+
+        public String getValue() {
+            return _value;
+        }
+
+        /**
+         * Visitor dispatch
+         */
+        public void visit(ResourceVisitor rv) {
+            rv.visitPropertyDesc(this);
+        }
+
+    }
+
+    public static class JREDesc implements ResourceType {
+
+        private String _version;
+        private long _maxHeap;
+        private long _minHeap;
+        private String _vmargs;
+        private ResourcesDesc _resourceDesc;
+        private JNLPDesc _extensioDesc;
+        private String _archList;
+
+        /*
+         * Constructor to create new instance based on the requirements from JNLP file.
+         */
+        public JREDesc(String version, long minHeap, long maxHeap, String vmargs,
+                       ResourcesDesc resourcesDesc, String archList) {
+
+            _version = version;
+            _maxHeap = maxHeap;
+            _minHeap = minHeap;
+            _vmargs = vmargs;
+            _resourceDesc = resourcesDesc;
+            _extensioDesc = null;
+            _archList = archList;
+        }
+
+        public String[] getArchList() {
+            return GeneralUtil.getStringList(_archList);
+        }
+
+        public String getVersion() {
+            return _version;
+        }
+
+        public long getMinHeap() {
+            return _minHeap;
+        }
+
+        public long getMaxHeap() {
+            return _maxHeap;
+        }
+
+        public String getVmArgs() {
+            return _vmargs;
+        }
+
+        public String[] getVmArgsList() {
+            return GeneralUtil.getStringList(_vmargs);
+        }
+
+        public ResourcesDesc getNestedResources() {
+            return _resourceDesc;
+        }
+
+        public JNLPDesc getExtensionDesc() {
+            return _extensioDesc;
+        }
+
+        public void setExtensionDesc(JNLPDesc ld) {
+            _extensioDesc = ld;
+        }
+
+        /* visitor dispatch */
+        public void visit(ResourceVisitor rv) {
+            rv.visitJREDesc(this);
+        }
+    }
+
+    public static class Property implements Cloneable {
+
+        public static final String JNLP_VERSION_ENABLED = "jnlp.versionEnabled";
+
+        String key;
+        String value;
+
+        public Property(String spec) {
+            spec = spec.trim();
+            if (!spec.startsWith("-D") || spec.length() < 3) {
+                throw new IllegalArgumentException("Property invalid");
+            }
+
+            int endKey = spec.indexOf("=");
+            if (endKey < 0) {
+                // it's legal to have no assignment
+                this.key = spec.substring(2); // skip "-D"
+                this.value = "";
+            } else {
+                this.key = spec.substring(2, endKey);
+                this.value = spec.substring(endKey + 1);
+            }
+        }
+
+        public static Property createProperty(String spec) {
+            Property prop = null;
+            try {
+                prop = new Property(spec);
+            } catch (IllegalArgumentException iae) {
+            }
+            return prop;
+        }
+
+        public Property(String key, String value) {
+            this.key = key;
+            if (value != null) {
+                this.value = value;
+            } else {
+                this.value = "";
+            }
+        }
+
+        public String getKey() {
+            return key;
+        }
+
+        public String getValue() {
+            return value;
+        }
+
+        // @return String representation, unquoted, unified presentation
+        public String toString() {
+            if (value.length() == 0) {
+                return "-D" + key;
+            }
+            return "-D" + key + "=" + value;
+        }
+
+        public void addTo(Properties props) {
+            props.setProperty(key, value);
+        }
+
+        // Hash Object
+        public boolean equals(Object o) {
+            if (!(o instanceof Property)) {
+                return false;
+            }
+            Property op = (Property) o;
+            int hashTheirs = op.hashCode();
+            int hashThis = hashCode();
+            return hashTheirs == hashThis;
+        }
+
+        public int hashCode() {
+            return key.hashCode();
+        }
+
+        private static List<Object> jnlpProps = Arrays.asList(new Object[]{
+            JNLP_VERSION_ENABLED
+        });
+
+        public static boolean isJnlpProperty(String spec) {
+            try {
+                Property p = new Property(spec);
+                return isJnlpPropertyKey(p.getKey());
+            } catch (Exception e) {
+                return false;
+            }
+        }
+
+        public static boolean isJnlpPropertyKey(String key) {
+            return key != null && jnlpProps.contains(key);
+        }
+    }
+
+    public static class ExtensionDesc implements ResourceType {
+        // Tag elements
+
+        private final URL _location;
+        private final String _locationString;
+        private final String _version;
+        private final URL _codebase;
+
+        // Link to launchDesc
+        private JNLPDesc _extensionLd; // Link to launchDesc for extension
+
+        public ExtensionDesc(URL location, String version) {
+            _location = location;
+            _locationString = GeneralUtil.toNormalizedString(location);
+            _version = version;
+            _codebase = GeneralUtil.asPathURL(GeneralUtil.getBase(location));
+            _extensionLd = null;
+        }
+
+        public boolean isInstaller() throws Exception {
+            if (getExtensionDesc() != null) {
+                return _extensionLd.isInstaller();
+            }
+            return false;
+        }
+
+        public URL getLocation() {
+            return _location;
+        }
+
+        public String getVersionLocation() throws Exception {
+            if (getVersion() == null) {
+                return _locationString;
+            } else {
+                return GeneralUtil.toNormalizedString(GeneralUtil.getEmbeddedVersionURL(getLocation(), getVersion()));
+            }
+        }
+
+        public String getVersion() {
+            return _version;
+        }
+
+        public URL getCodebase() {
+            return _codebase;
+        }
+
+        /*
+         * Information about the resources
+         */
+        public JNLPDesc getExtensionDesc() throws Exception {
+            if (_extensionLd == null) {
+                byte[] bits = HTTPHelper.getJNLPBits(getVersionLocation(), _locationString);
+                _extensionLd = XMLFormat.parse(bits, getCodebase(), getVersionLocation());
+            }
+            return _extensionLd;
+        }
+
+        public void setExtensionDesc(JNLPDesc desc) {
+            _extensionLd = desc;
+        }
+
+        /**
+         * Visitor dispatch
+         */
+        public void visit(ResourceVisitor rv) throws Exception {
+            rv.visitExtensionDesc(this);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/VersionID.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,313 @@
+/*
+ * Copyright (c) 2006, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jnlp.converter.parser;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ *  VersionID contains a JNLP version ID.
+ *
+ *  The VersionID also contains a prefix indicator that can
+ *  be used when stored with a VersionString
+ *
+ */
+public class VersionID implements Comparable<VersionID> {
+    private final String[] _tuple;           // Array of Integer or String objects
+    private final boolean  _usePrefixMatch;  // star (*) prefix
+    private final boolean  _useGreaterThan;  // plus (+) greather-than
+    private final boolean  _isCompound;      // and (&) operator
+    private final VersionID _rest;           // remaining part after the &
+
+    /**
+     * Creates a VersionID object from a given <code>String</code>.
+     * @param str version string to parse
+     */
+    public VersionID(String str) {
+        if (str == null || str.length() == 0) {
+            _tuple = new String[0];
+            _useGreaterThan = false;
+            _usePrefixMatch = false;
+            _isCompound = false;
+            _rest = null;
+            return;
+        }
+
+        // Check for compound
+        int amp = str.indexOf("&");
+        if (amp >= 0) {
+            _isCompound = true;
+            VersionID firstPart = new VersionID(str.substring(0, amp));
+            _rest = new VersionID(str.substring(amp+1));
+            _tuple = firstPart._tuple;
+            _usePrefixMatch = firstPart._usePrefixMatch;
+            _useGreaterThan = firstPart._useGreaterThan;
+        } else {
+            _isCompound = false;
+            _rest = null;
+            // Check for postfix
+            if (str.endsWith("+")) {
+                _useGreaterThan = true;
+                _usePrefixMatch = false;
+                str = str.substring(0, str.length() - 1);
+            } else if (str.endsWith("*")) {
+                _useGreaterThan = false;
+                _usePrefixMatch = true;
+                str = str.substring(0, str.length() - 1);
+            } else {
+                _useGreaterThan = false;
+                _usePrefixMatch = false;
+            }
+
+            ArrayList<String> list = new ArrayList<>();
+            int start = 0;
+            for (int i = 0; i < str.length(); i++) {
+                // Split at each separator character
+                if (".-_".indexOf(str.charAt(i)) != -1) {
+                    if (start < i) {
+                        String value = str.substring(start, i);
+                        list.add(value);
+                    }
+                    start = i + 1;
+                }
+            }
+            if (start < str.length()) {
+                list.add(str.substring(start, str.length()));
+            }
+            _tuple = list.toArray(new String[0]);
+        }
+    }
+
+    /** @return true if no flags are set */
+    public boolean isSimpleVersion() {
+        return !_useGreaterThan && !_usePrefixMatch && !_isCompound;
+    }
+
+    /** Match 'this' versionID against vid.
+     *  The _usePrefixMatch/_useGreaterThan flag is used to determine if a
+     *  prefix match of an exact match should be performed
+     *  if _isCompound, must match _rest also.
+     */
+    public boolean match(VersionID vid) {
+        if (_isCompound) {
+            if (!_rest.match(vid)) {
+                return false;
+            }
+        }
+        return (_usePrefixMatch) ? this.isPrefixMatchTuple(vid) :
+            (_useGreaterThan) ? vid.isGreaterThanOrEqualTuple(this) :
+                matchTuple(vid);
+    }
+
+    /** Compares if two version IDs are equal */
+    @Override
+    public boolean equals(Object o) {
+        if (matchTuple(o)) {
+             VersionID ov = (VersionID) o;
+             if (_rest == null || _rest.equals(ov._rest)) {
+                if ((_useGreaterThan == ov._useGreaterThan) &&
+                    (_usePrefixMatch == ov._usePrefixMatch)) {
+                        return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /** Computes a hash code for a VersionID */
+    @Override
+    public int hashCode() {
+        boolean first = true;
+        int hashCode = 0;
+        for (String tuple : _tuple) {
+            if (first) {
+                first = false;
+                hashCode = tuple.hashCode();
+            } else {
+                hashCode = hashCode ^ tuple.hashCode();
+            }
+        }
+        return hashCode;
+    }
+
+    /** Compares if two version IDs are equal */
+    private boolean matchTuple(Object o) {
+        // Check for null and type
+        if (o == null || !(o instanceof VersionID)) {
+            return false;
+        }
+        VersionID vid = (VersionID) o;
+
+        // Normalize arrays
+        String[] t1 = normalize(_tuple, vid._tuple.length);
+        String[] t2 = normalize(vid._tuple, _tuple.length);
+
+        // Check contents
+        for (int i = 0; i < t1.length; i++) {
+            Object o1 = getValueAsObject(t1[i]);
+            Object o2 = getValueAsObject(t2[i]);
+            if (!o1.equals(o2)) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    private Object getValueAsObject(String value) {
+        if (value.length() > 0 && value.charAt(0) != '-') {
+            try {
+                return Integer.valueOf(value);
+            } catch (NumberFormatException nfe) {
+                /* fall through */
+            }
+        }
+        return value;
+    }
+
+    public boolean isGreaterThan(VersionID vid) {
+        if (vid == null) {
+            return false;
+        }
+        return isGreaterThanOrEqualHelper(vid, false, true);
+    }
+
+    public boolean isGreaterThanOrEqual(VersionID vid) {
+        if (vid == null) {
+            return false;
+        }
+        return isGreaterThanOrEqualHelper(vid, true, true);
+    }
+
+    boolean isGreaterThanOrEqualTuple(VersionID vid) {
+        return isGreaterThanOrEqualHelper(vid, true, false);
+    }
+
+    /** Compares if 'this' is greater than vid */
+    private boolean isGreaterThanOrEqualHelper(VersionID vid,
+        boolean allowEqual, boolean useRest) {
+
+        if (useRest && _isCompound) {
+            if (!_rest.isGreaterThanOrEqualHelper(vid, allowEqual, true)) {
+                return false;
+            }
+        }
+        // Normalize the two strings
+        String[] t1 = normalize(_tuple, vid._tuple.length);
+        String[] t2 = normalize(vid._tuple, _tuple.length);
+
+        for (int i = 0; i < t1.length; i++) {
+            // Compare current element
+            Object e1 = getValueAsObject(t1[i]);
+            Object e2 = getValueAsObject(t2[i]);
+            if (e1.equals(e2)) {
+                // So far so good
+            } else {
+                if (e1 instanceof Integer && e2 instanceof Integer) {
+                    // if both can be parsed as ints, compare ints
+                    return ((Integer)e1).intValue() > ((Integer)e2).intValue();
+                } else {
+                    if (e1 instanceof Integer)  {
+                        return false; // e1 (numeric) < e2 (non-numeric)
+                    } else if (e2 instanceof Integer) {
+                        return true; // e1 (non-numeric) > e2 (numeric)
+                    }
+
+                    String s1 = t1[i];
+                    String s2 = t2[i];
+
+                    return s1.compareTo(s2) > 0;
+                }
+
+            }
+        }
+        // If we get here, they are equal
+        return allowEqual;
+    }
+
+    /** Checks if 'this' is a prefix of vid */
+    private boolean isPrefixMatchTuple(VersionID vid) {
+
+        // Make sure that vid is at least as long as the prefix
+        String[] t2 = normalize(vid._tuple, _tuple.length);
+
+        for (int i = 0; i < _tuple.length; i++) {
+            Object e1 = _tuple[i];
+            Object e2 = t2[i];
+            if (e1.equals(e2)) {
+                // So far so good
+            } else {
+                // Not a prefix
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /** Normalize an array to a certain lengh */
+    private String[] normalize(String[] list, int minlength) {
+        if (list.length < minlength) {
+            // Need to do padding
+            String[] newlist = new String[minlength];
+            System.arraycopy(list, 0, newlist, 0, list.length);
+            Arrays.fill(newlist, list.length, newlist.length, "0");
+            return newlist;
+        } else {
+            return list;
+        }
+    }
+
+    @Override
+    public int compareTo(VersionID o) {
+        if (o == null || !(o instanceof VersionID)) {
+            return -1;
+        }
+        VersionID vid = o;
+        return equals(vid) ? 0 : (isGreaterThanOrEqual(vid) ? 1 : -1);
+    }
+
+    /** Show it as a string */
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < _tuple.length - 1; i++) {
+            sb.append(_tuple[i]);
+            sb.append('.');
+        }
+        if (_tuple.length > 0) {
+            sb.append(_tuple[_tuple.length - 1]);
+        }
+        if (_useGreaterThan) {
+            sb.append('+');
+        }
+        if (_usePrefixMatch) {
+            sb.append('*');
+        }
+        if (_isCompound) {
+            sb.append("&");
+            sb.append(_rest);
+        }
+        return sb.toString();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/VersionString.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2006, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jnlp.converter.parser;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.StringTokenizer;
+
+/*
+ * Utility class that knows to handle version strings
+ * A version string is of the form:
+ *
+ *  (version-id ('+'?) ' ') *
+ *
+ */
+public class VersionString {
+    private final ArrayList<VersionID> _versionIds;
+
+    /** Constructs a VersionString object from string */
+    public VersionString(String vs) {
+        _versionIds = new ArrayList<>();
+        if (vs != null) {
+            StringTokenizer st = new StringTokenizer(vs, " ", false);
+            while (st.hasMoreElements()) {
+                // Note: The VersionID class takes care of a postfixed '+'
+                _versionIds.add(new VersionID(st.nextToken()));
+            }
+        }
+    }
+
+    public VersionString(VersionID id) {
+        _versionIds = new ArrayList<>();
+        if (id != null) {
+            _versionIds.add(id);
+        }
+    }
+
+    public boolean isSimpleVersion() {
+        if (_versionIds.size() == 1) {
+            return _versionIds.get(0).isSimpleVersion();
+        }
+        return false;
+    }
+
+    /** Check if this VersionString object contains the VersionID m */
+    public boolean contains(VersionID m) {
+        for (int i = 0; i < _versionIds.size(); i++) {
+            VersionID vi = _versionIds.get(i);
+            boolean check = vi.match(m);
+            if (check) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /** Check if this VersionString object contains the VersionID m, given as a string */
+    public boolean contains(String versionid) {
+        return contains(new VersionID(versionid));
+    }
+
+    /** Check if this VersionString object contains anything greater than m */
+    public boolean containsGreaterThan(VersionID m) {
+        for (int i = 0; i < _versionIds.size(); i++) {
+            VersionID vi = _versionIds.get(i);
+            boolean check = vi.isGreaterThan(m);
+            if (check) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /** Check if this VersionString object contains anything greater than the VersionID m, given as a string */
+    public boolean containsGreaterThan(String versionid) {
+        return containsGreaterThan(new VersionID(versionid));
+    }
+
+    /** Check if the versionString 'vs' contains the VersionID 'vi' */
+    public static boolean contains(String vs, String vi) {
+        return (new VersionString(vs)).contains(vi);
+    }
+
+    /** Pretty-print object */
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < _versionIds.size(); i++) {
+            sb.append(_versionIds.get(i).toString());
+            sb.append(' ');
+        }
+        return sb.toString();
+    }
+
+    public List<VersionID> getAllVersionIDs() {
+        return _versionIds;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/XMLFormat.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,659 @@
+/*
+ * Copyright (c) 2006, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jnlp.converter.parser;
+
+import java.net.URL;
+import java.util.Arrays;
+import java.util.ArrayList;
+import jnlp.converter.JNLPConverter;
+import jnlp.converter.parser.exception.MissingFieldException;
+import jnlp.converter.parser.exception.BadFieldException;
+import jnlp.converter.parser.exception.JNLParseException;
+import jnlp.converter.parser.xml.XMLEncoding;
+import jnlp.converter.parser.xml.XMLParser;
+import jnlp.converter.parser.xml.XMLNode;
+import jnlp.converter.parser.JNLPDesc.AssociationDesc;
+import jnlp.converter.parser.JNLPDesc.IconDesc;
+import jnlp.converter.parser.JNLPDesc.InformationDesc;
+import jnlp.converter.parser.JNLPDesc.ShortcutDesc;
+import jnlp.converter.parser.ResourcesDesc.JARDesc;
+import jnlp.converter.parser.ResourcesDesc.JREDesc;
+import jnlp.converter.Log;
+import jnlp.converter.parser.ResourcesDesc.ExtensionDesc;
+import jnlp.converter.parser.ResourcesDesc.PropertyDesc;
+import org.xml.sax.SAXParseException;
+
+public class XMLFormat {
+
+    public static XMLNode parseBits(byte[] bits) throws JNLParseException {
+        return parse(decode(bits));
+    }
+
+    private static String decode(byte[] bits) throws JNLParseException {
+        try {
+            return XMLEncoding.decodeXML(bits);
+        } catch (Exception e) {
+            throw new JNLParseException(e,
+                "exception determining encoding of jnlp file", 0);
+        }
+    }
+
+    private static XMLNode parse(String source) throws JNLParseException {
+        try {
+            return (new XMLParser(source).parse());
+        } catch (SAXParseException spe) {
+            throw new JNLParseException(spe,
+                        "exception parsing jnlp file", spe.getLineNumber());
+        } catch (Exception e) {
+            throw new JNLParseException(e,
+                        "exception parsing jnlp file", 0);
+        }
+    }
+
+    /**
+     * thisCodebase, if set, is used to determine the codebase,
+     *     if JNLP codebase is not absolute.
+     *
+     * @param thisCodebase base URL of this JNLPDesc location
+     */
+    public static JNLPDesc parse(byte[] bits, URL thisCodebase, String jnlp)
+            throws Exception {
+
+        JNLPDesc jnlpd = new JNLPDesc();
+        String source = decode(bits).trim();
+        XMLNode root = parse(source);
+
+        if (root == null || root.getName() == null) {
+            throw new JNLParseException(null, null, 0);
+        }
+
+        // Check that root element is a <jnlp> tag
+        if (!root.getName().equals("jnlp")) {
+            throw (new MissingFieldException(source, "<jnlp>"));
+        }
+
+        // Read <jnlp> attributes (path is empty, i.e., "")
+        // (spec, version, codebase, href)
+        String specVersion = XMLUtils.getAttribute(root, "", "spec", "1.0+");
+        jnlpd.setSpecVersion(specVersion);
+        String version = XMLUtils.getAttribute(root, "", "version");
+        jnlpd.setVersion(version);
+
+        // Make sure the codebase URL ends with a '/'.
+        //
+        // Regarding the JNLP spec,
+        // the thisCodebase is used to determine the codebase.
+        //      codebase = new URL(thisCodebase, codebase)
+        URL codebase = GeneralUtil.asPathURL(XMLUtils.getAttributeURL(source,
+            thisCodebase, root, "", "codebase"));
+        if (codebase == null && thisCodebase != null) {
+            codebase = thisCodebase;
+        }
+        jnlpd.setCodebase(codebase.toExternalForm());
+
+        // Get href for JNLP file
+        URL href = XMLUtils.getAttributeURL(source, codebase, root, "", "href");
+        jnlpd.setHref(href.toExternalForm());
+
+        // Read <security> attributes
+        if (XMLUtils.isElementPath(root, "<security><all-permissions>")) {
+            jnlpd.setIsSandbox(false);
+        } else if (XMLUtils.isElementPath(root,
+                "<security><j2ee-application-client-permissions>")) {
+            jnlpd.setIsSandbox(false);
+        }
+
+        // We can be fxapp, and also be applet, or application, or neither
+        boolean isFXApp = false;
+        boolean isApplet = false;
+        if (XMLUtils.isElementPath(root, "<javafx-desc>")) {
+            // no new type for javafx-desc - needs one of the others
+            buildFXAppDesc(source, root, "<javafx-desc>", jnlpd);
+            jnlpd.setIsFXApp(true);
+            isFXApp = true;
+        }
+
+        /*
+         * Note - the jnlp specification says there must be exactly one of
+         * the descriptor types.  This code has always violated (or at least
+         * not checked for) that condition.
+         * Instead it uses precedent order app, component, installer, applet
+         * and ignores any other descriptors given.
+         */
+        if (XMLUtils.isElementPath(root, "<application-desc>")) {
+            buildApplicationDesc(source, root, jnlpd);
+        } else if (XMLUtils.isElementPath(root, "<component-desc>")) {
+            jnlpd.setIsLibrary(true);
+        } else if (XMLUtils.isElementPath(root, "<installer-desc>")) {
+            Log.warning("<installer-desc> is not supported and will be ignored in " + jnlp);
+            jnlpd.setIsInstaller(true);
+        } else if (XMLUtils.isElementPath(root, "<applet-desc>")) {
+            isApplet = true;
+        } else {
+            if (!isFXApp) {
+                throw (new MissingFieldException(source,
+                    "<jnlp>(<application-desc>|<applet-desc>|" +
+                    "<installer-desc>|<component-desc>)"));
+            }
+        }
+
+        if (isApplet && !isFXApp) {
+            Log.error("Applet based applications deployed with <applet-desc> element are not supported.");
+        }
+
+        if (!jnlpd.isLibrary() && !jnlpd.isInstaller()) {
+            buildInformationDesc(source, codebase, root, jnlpd);
+        }
+
+        if (!jnlpd.isInstaller()) {
+            buildResourcesDesc(source, codebase, root, false, jnlpd);
+        }
+
+        if (!jnlpd.isLibrary() && !jnlpd.isInstaller()) {
+            jnlpd.parseResourceDesc();
+        }
+
+        if (!jnlpd.isInstaller()) {
+            if (jnlpd.isSandbox()) {
+                if (jnlpd.isLibrary()) {
+                    Log.warning(jnlp + " is sandbox extension. JNLPConverter does not support sandbox environment and converted application will run without security manager.");
+                } else {
+                    Log.warning("This is sandbox Web-Start application. JNLPConverter does not support sandbox environment and converted application will run without security manager.");
+                }
+            }
+        }
+
+        return jnlpd;
+    }
+
+    /**
+     * Create a combine informationDesc in the two informationDesc.
+     * The information present in id1 overwrite the information present in id2
+     */
+    private static InformationDesc combineInformationDesc(
+                                   InformationDesc id1, InformationDesc id2) {
+        if (id1 == null) {
+            return id2;
+        }
+        if (id2 == null) {
+            return id1;
+        }
+
+        String t1 = id1.getTitle();
+        String title  = (t1 != null && t1.length() > 0) ?
+            t1 : id2.getTitle();
+        String v1 = id1.getVendor();
+        String vendor = (v1 != null && v1.length() > 0) ?
+            v1 : id2.getVendor();
+
+        /** Copy descriptions */
+        String[] descriptions = new String[InformationDesc.NOF_DESC];
+        for (int i = 0; i < descriptions.length; i++) {
+            descriptions[i] = (id1.getDescription(i) != null)
+                    ? id1.getDescription(i) : id2.getDescription(i);
+        }
+
+        /** Icons */
+        ArrayList<IconDesc> iconList = new ArrayList<>();
+        if (id2.getIcons() != null) {
+            iconList.addAll(Arrays.asList(id2.getIcons()));
+        }
+        if (id1.getIcons() != null) {
+            iconList.addAll(Arrays.asList(id1.getIcons()));
+        }
+        IconDesc[] icons = new IconDesc[iconList.size()];
+        icons = iconList.toArray(icons);
+
+        ShortcutDesc hints = (id1.getShortcut() != null) ?
+                             id1.getShortcut() : id2.getShortcut();
+
+        AssociationDesc[] asd = ( AssociationDesc[] ) addArrays(
+            (Object[])id1.getAssociations(), (Object[])id2.getAssociations());
+
+        return new InformationDesc(title,
+                                   vendor,
+                                   descriptions,
+                                   icons,
+                                   hints,
+                                   asd);
+    }
+
+    /** Extract data from <information> tag */
+    private static void buildInformationDesc(final String source, final URL codebase, XMLNode root, JNLPDesc jnlpd)
+        throws MissingFieldException, BadFieldException {
+        final ArrayList<InformationDesc> list = new ArrayList<>();
+
+        // Iterates over all <information> nodes ignoring the type
+        XMLUtils.visitElements(root,
+            "<information>", new XMLUtils.ElementVisitor() {
+            @Override
+            public void visitElement(XMLNode e) throws
+                BadFieldException, MissingFieldException {
+
+                // Check for right os, arch, and locale
+                String[] os = GeneralUtil.getStringList(
+                            XMLUtils.getAttribute(e, "", "os", null));
+                String[] arch = GeneralUtil.getStringList(
+                            XMLUtils.getAttribute(e, "", "arch", null));
+                String[] locale = GeneralUtil.getStringList(
+                            XMLUtils.getAttribute(e, "", "locale", null));
+                if (GeneralUtil.prefixMatchStringList(
+                                os, GeneralUtil.getOSFullName()) &&
+                    GeneralUtil.prefixMatchArch(arch) &&
+                    matchDefaultLocale(locale))
+                {
+                    // Title, vendor
+                    String title = XMLUtils.getElementContents(e, "<title>");
+                    String vendor = XMLUtils.getElementContents(e, "<vendor>");
+
+                    // Descriptions
+                    String[] descriptions =
+                                new String[InformationDesc.NOF_DESC];
+                    descriptions[InformationDesc.DESC_DEFAULT] =
+                        XMLUtils.getElementContentsWithAttribute(
+                        e, "<description>", "kind", "", null);
+                    descriptions[InformationDesc.DESC_ONELINE] =
+                        XMLUtils.getElementContentsWithAttribute(
+                        e, "<description>", "kind", "one-line", null);
+                    descriptions[InformationDesc.DESC_SHORT] =
+                        XMLUtils.getElementContentsWithAttribute(
+                        e, "<description>", "kind", "short", null);
+                    descriptions[InformationDesc.DESC_TOOLTIP] =
+                        XMLUtils.getElementContentsWithAttribute(
+                        e, "<description>", "kind", "tooltip", null);
+
+                    // Icons
+                    IconDesc[] icons = getIconDescs(source, codebase, e);
+
+                    // Shortcut hints
+                    ShortcutDesc shortcuts = getShortcutDesc(e);
+
+                    // Association hints
+                    AssociationDesc[] associations = getAssociationDesc(
+                                                        source, codebase, e);
+
+                    list.add(new InformationDesc(
+                        title, vendor, descriptions, icons,
+                        shortcuts, associations));
+                }
+            }
+        });
+
+        /* Combine all information desc. information in a single one for
+         * the current locale using the following priorities:
+         *   1. locale == language_country_variant
+         *   2. locale == lauguage_country
+         *   3. locale == lauguage
+         *   4. no or empty locale
+         */
+        InformationDesc normId = new InformationDesc(null, null, null, null, null, null);
+        for (InformationDesc id : list) {
+            normId = combineInformationDesc(id, normId);
+        }
+
+        jnlpd.setTitle(normId.getTitle());
+        jnlpd.setVendor(normId.getVendor());
+        jnlpd.setDescriptions(normId.getDescription());
+        jnlpd.setIcons(normId.getIcons());
+        jnlpd.setShortcuts(normId.getShortcut());
+        jnlpd.setAssociations(normId.getAssociations());
+    }
+
+    private static Object[] addArrays (Object[] a1, Object[] a2) {
+        if (a1 == null) {
+            return a2;
+        }
+        if (a2 == null) {
+            return a1;
+        }
+        ArrayList<Object> list = new ArrayList<>();
+        int i;
+        for (i=0; i<a1.length; list.add(a1[i++]));
+        for (i=0; i<a2.length; list.add(a2[i++]));
+        return list.toArray(a1);
+    }
+
+    public static boolean matchDefaultLocale(String[] localeStr) {
+        return GeneralUtil.matchLocale(localeStr, GeneralUtil.getDefaultLocale());
+    }
+
+    /** Extract data from <resources> tag. There is only one. */
+    static void buildResourcesDesc(final String source,
+            final URL codebase, XMLNode root, final boolean ignoreJres, JNLPDesc jnlpd)
+            throws MissingFieldException, BadFieldException {
+        // Extract classpath directives
+        final ResourcesDesc rdesc = new ResourcesDesc();
+
+        // Iterate over all entries
+        XMLUtils.visitElements(root, "<resources>",
+                new XMLUtils.ElementVisitor() {
+            @Override
+            public void visitElement(XMLNode e)
+                    throws MissingFieldException, BadFieldException {
+                // Check for right os, archictecture, and locale
+                String[] os = GeneralUtil.getStringList(
+                        XMLUtils.getAttribute(e, "", "os", null));
+                final String arch = XMLUtils.getAttribute(e, "", "arch", null);
+                String[] locale = GeneralUtil.getStringList(
+                        XMLUtils.getAttribute(e, "", "locale", null));
+                if (GeneralUtil.prefixMatchStringList(
+                        os, GeneralUtil.getOSFullName())
+                        && matchDefaultLocale(locale)) {
+                    // Now visit all children in this node
+                    XMLUtils.visitChildrenElements(e,
+                            new XMLUtils.ElementVisitor() {
+                        @Override
+                        public void visitElement(XMLNode e2)
+                                throws MissingFieldException, BadFieldException {
+                            handleResourceElement(source, codebase,
+                                    e2, rdesc, ignoreJres, arch, jnlpd);
+                        }
+                    });
+                }
+            }
+        });
+
+        if (!rdesc.isEmpty()) {
+            jnlpd.setResourcesDesc(rdesc);
+        }
+    }
+
+    private static IconDesc[] getIconDescs(final String source,
+            final URL codebase, XMLNode e)
+            throws MissingFieldException, BadFieldException {
+        final ArrayList<IconDesc> answer = new ArrayList<>();
+        XMLUtils.visitElements(e, "<icon>", new XMLUtils.ElementVisitor() {
+            @Override
+            public void visitElement(XMLNode icon) throws
+                    MissingFieldException, BadFieldException {
+                String kindStr = XMLUtils.getAttribute(icon, "", "kind", "");
+                URL href = XMLUtils.getRequiredURL(source, codebase, icon, "", "href");
+
+                if (href != null) {
+                    if (!JNLPConverter.isIconSupported(href.toExternalForm())) {
+                        return;
+                    }
+                }
+
+                int kind;
+                if (kindStr == null || kindStr.isEmpty() || kindStr.equals("default")) {
+                    kind = IconDesc.ICON_KIND_DEFAULT;
+                } else if (kindStr.equals("shortcut")) {
+                    kind = IconDesc.ICON_KIND_SHORTCUT;
+                } else {
+                    Log.warning("Ignoring unsupported icon \"" + href + "\" with kind \"" + kindStr + "\".");
+                    return;
+                }
+
+                answer.add(new IconDesc(href, kind));
+            }
+        });
+        return answer.toArray(new IconDesc[answer.size()]);
+    }
+
+    private static ShortcutDesc getShortcutDesc(XMLNode e)
+                throws MissingFieldException, BadFieldException {
+        final ArrayList<ShortcutDesc> shortcuts = new ArrayList<>();
+
+        XMLUtils.visitElements(e, "<shortcut>", new XMLUtils.ElementVisitor() {
+            @Override
+            public void visitElement(XMLNode shortcutNode)
+                throws MissingFieldException, BadFieldException {
+                boolean desktopHinted =
+                    XMLUtils.isElementPath(shortcutNode, "<desktop>");
+                boolean menuHinted =
+                    XMLUtils.isElementPath(shortcutNode, "<menu>");
+                String submenuHinted =
+                    XMLUtils.getAttribute(shortcutNode, "<menu>", "submenu");
+                shortcuts.add(new ShortcutDesc(desktopHinted, menuHinted, submenuHinted));
+            }
+        });
+
+        if (shortcuts.size() > 0) {
+            return shortcuts.get(0);
+        }
+        return null;
+    }
+
+    private static AssociationDesc[] getAssociationDesc(final String source,
+        final URL codebase, XMLNode e)
+                throws MissingFieldException, BadFieldException {
+        final ArrayList<AssociationDesc> answer = new ArrayList<>();
+        XMLUtils.visitElements(e, "<association>",
+            new XMLUtils.ElementVisitor() {
+            @Override
+            public void visitElement(XMLNode node)
+                throws MissingFieldException, BadFieldException {
+
+                String extensions = XMLUtils.getAttribute(
+                                       node, "", "extensions");
+
+                String mimeType = XMLUtils.getAttribute(
+                                       node, "", "mime-type");
+                String description = XMLUtils.getElementContents(
+                                        node, "<description>");
+
+                URL icon = XMLUtils.getAttributeURL(
+                                source, codebase, node, "<icon>", "href");
+
+                if (!JNLPConverter.isIconSupported(icon.toExternalForm())) {
+                    icon = null;
+                }
+
+                if (extensions == null && mimeType == null) {
+                    throw new MissingFieldException(source,
+                                 "<association>(<extensions><mime-type>)");
+                } else if (extensions == null) {
+                    throw new MissingFieldException(source,
+                                     "<association><extensions>");
+                } else if (mimeType == null) {
+                    throw new MissingFieldException(source,
+                                     "<association><mime-type>");
+                }
+
+                // don't support uppercase extension and mime-type on gnome.
+                if ("gnome".equals(System.getProperty("sun.desktop"))) {
+                    extensions = extensions.toLowerCase();
+                    mimeType = mimeType.toLowerCase();
+                }
+
+                answer.add(new AssociationDesc(extensions, mimeType,
+                                                description, icon));
+            }
+        });
+        return answer.toArray(
+                new AssociationDesc[answer.size()]);
+    }
+
+    /** Handle the individual entries in a resource desc */
+    private static void handleResourceElement(String source, URL codebase,
+        XMLNode e, ResourcesDesc rdesc, boolean ignoreJres, String arch, JNLPDesc jnlpd)
+        throws MissingFieldException, BadFieldException {
+
+        String tag = e.getName();
+
+        boolean matchArch = GeneralUtil.prefixMatchArch(
+            GeneralUtil.getStringList(arch));
+
+
+        if (matchArch && (tag.equals("jar") || tag.equals("nativelib"))) {
+            /*
+             * jar/nativelib elements
+             */
+            URL href = XMLUtils.getRequiredURL(source, codebase, e, "", "href");
+            String version = XMLUtils.getAttribute(e, "", "version", null);
+
+            String mainStr = XMLUtils.getAttribute(e, "", "main");
+            boolean isNativeLib = tag.equals("nativelib");
+
+            boolean isMain = "true".equalsIgnoreCase(mainStr);
+
+            JARDesc jd = new JARDesc(href, version, isMain, isNativeLib, rdesc);
+            rdesc.addResource(jd);
+        } else if (matchArch && tag.equals("property")) {
+            /*
+             *  property tag
+             */
+            String name  = XMLUtils.getRequiredAttribute(source, e, "", "name");
+            String value = XMLUtils.getRequiredAttributeEmptyOK(
+                    source, e, "", "value");
+
+            rdesc.addResource(new PropertyDesc(name, value));
+        } else if (matchArch && tag.equals("extension")) {
+            URL href = XMLUtils.getRequiredURL(source, codebase, e, "", "href");
+            String version = XMLUtils.getAttribute(e, "", "version", null);
+            rdesc.addResource(new ExtensionDesc(href, version));
+        } else if ((tag.equals("java") || tag.equals("j2se")) && !ignoreJres) {
+            /*
+             * j2se element
+             */
+            String version  =
+                XMLUtils.getRequiredAttribute(source, e, "", "version");
+            String minheapstr =
+                XMLUtils.getAttribute(e, "", "initial-heap-size");
+            String maxheapstr =
+                XMLUtils.getAttribute(e, "", "max-heap-size");
+
+            String vmargs =
+                XMLUtils.getAttribute(e, "", "java-vm-args");
+
+            if (jnlpd.isJRESet()) {
+                if (vmargs == null) {
+                    vmargs = "none";
+                }
+                Log.warning("Ignoring repeated element <" + tag + "> with version " + version +
+                        " and java-vm-args: " + vmargs);
+                return;
+            }
+
+            long minheap = GeneralUtil.heapValToLong(minheapstr);
+            long maxheap = GeneralUtil.heapValToLong(maxheapstr);
+
+            ResourcesDesc cbs = null;
+            buildResourcesDesc(source, codebase, e, true, null);
+
+            // JRE
+            JREDesc jreDesc = new JREDesc(
+                version,
+                minheap,
+                maxheap,
+                vmargs,
+                cbs,
+                arch);
+
+            rdesc.addResource(jreDesc);
+
+            jnlpd.setIsJRESet(true);
+        }
+    }
+
+    /** Extract data from the application-desc tag */
+    private static void buildApplicationDesc(final String source,
+        XMLNode root, JNLPDesc jnlpd) throws MissingFieldException, BadFieldException {
+
+        String mainclass = XMLUtils.getClassName(source, root,
+                           "<application-desc>", "main-class", false);
+        String appType = XMLUtils.getAttribute(root, "<application-desc>",
+                                               "type", "Java");
+        String progressclass  = XMLUtils.getClassName(source, root,
+                                "<application-desc>", "progress-class", false);
+        if (progressclass != null && !progressclass.isEmpty()) {
+            Log.warning("JNLPConverter does not support progress indication. \"" + progressclass + "\" will not be loaded and will be ignored.");
+        }
+
+        if (!("Java".equalsIgnoreCase(appType) ||
+            "JavaFx".equalsIgnoreCase(appType))) {
+            throw new BadFieldException(source, XMLUtils.getPathString(root) +
+                "<application-desc>type", appType);
+        }
+
+        if ("JavaFx".equalsIgnoreCase(appType)) {
+            jnlpd.setIsFXApp(true);
+        }
+
+        XMLUtils.visitElements(root, "<application-desc><argument>", new XMLUtils.ElementVisitor() {
+            @Override
+            public void visitElement(XMLNode e) throws MissingFieldException, BadFieldException {
+                String arg = XMLUtils.getElementContents(e, "", null);
+                if (arg == null) {
+                    throw new BadFieldException(source, XMLUtils.getPathString(e), "");
+                }
+                jnlpd.addArguments(arg);
+            }
+        });
+
+        XMLUtils.visitElements(root, "<application-desc><param>",
+            new XMLUtils.ElementVisitor() {
+            @Override
+            public void visitElement(XMLNode e) throws MissingFieldException,
+                BadFieldException {
+                String pn = XMLUtils.getRequiredAttribute(
+                            source, e, "", "name");
+                String pv = XMLUtils.getRequiredAttributeEmptyOK(
+                            source, e, "", "value");
+                jnlpd.setProperty(pn, pv);
+            }
+        });
+        jnlpd.setMainClass(mainclass, false);
+    }
+
+    /** Extract data from the javafx-desc tag */
+    private static void buildFXAppDesc(final String source,
+        XMLNode root, String element, JNLPDesc jnlpd)
+             throws MissingFieldException, BadFieldException {
+        String mainclass = XMLUtils.getClassName(source, root, element,
+                                                 "main-class", true);
+        String name = XMLUtils.getRequiredAttribute(source, root,
+                                        "<javafx-desc>", "name");
+
+        /* extract arguments */
+        XMLUtils.visitElements(root, "<javafx-desc><argument>", new XMLUtils.ElementVisitor() {
+            @Override
+            public void visitElement(XMLNode e) throws MissingFieldException, BadFieldException {
+                String arg = XMLUtils.getElementContents(e, "", null);
+                if (arg == null) {
+                    throw new BadFieldException(source, XMLUtils.getPathString(e), "");
+                }
+                jnlpd.addArguments(arg);
+            }
+        });
+
+        /* extract parameters */
+        XMLUtils.visitElements(root, "<javafx-desc><param>",
+            new XMLUtils.ElementVisitor() {
+            @Override
+            public void visitElement(XMLNode e) throws MissingFieldException,
+                BadFieldException {
+                String pn = XMLUtils.getRequiredAttribute(
+                            source, e, "", "name");
+                String pv = XMLUtils.getRequiredAttributeEmptyOK(
+                            source, e, "", "value");
+                jnlpd.setProperty(pn, pv);
+            }
+        });
+
+        jnlpd.setMainClass(mainclass, true);
+        jnlpd.setName(name);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/XMLUtils.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,328 @@
+/*
+ * Copyright (c) 2006, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jnlp.converter.parser;
+
+import java.net.URL;
+import java.net.MalformedURLException;
+
+import jnlp.converter.parser.exception.BadFieldException;
+import jnlp.converter.parser.exception.MissingFieldException;
+import jnlp.converter.parser.xml.XMLNode;
+
+/** Contains handy methods for looking up information
+ *  stored in XMLNodes.
+ */
+public class XMLUtils {
+
+    /** Returns the value of an integer attribute */
+    public static int getIntAttribute(String source, XMLNode root, String path, String name, int defaultvalue)
+            throws BadFieldException {
+        String value = getAttribute(root, path, name);
+        if (value == null) {
+            return defaultvalue;
+        }
+
+        try {
+            return Integer.parseInt(value);
+        } catch (NumberFormatException nfe) {
+            throw new BadFieldException(source, getPathString(root) + path + name, value);
+        }
+    }
+
+    /** Returns the value of a given attribute, or null if not set */
+    public static String getAttribute(XMLNode root, String path, String name)
+                                                      throws BadFieldException {
+        return getAttribute(root, path, name, null);
+    }
+
+    /** Returns the value of a given attribute */
+    public static String getRequiredAttributeEmptyOK(String source,
+        XMLNode root, String path, String name) throws MissingFieldException {
+        String value = null;
+        XMLNode elem = findElementPath(root, path);
+        if (elem != null) {
+            value = elem.getAttribute(name);
+        }
+        if (value == null) {
+            throw new MissingFieldException(source,
+                                            getPathString(root)+ path + name);
+        }
+        return value;
+    }
+
+    /*** Returns the value of an attribute, which must be a valid class name */
+    public static String getClassName(String source, XMLNode root,
+        String path, String name, boolean required)
+            throws BadFieldException, MissingFieldException {
+
+        String className;
+        if (required) {
+            className = getRequiredAttribute(source, root, path, name);
+        } else {
+            className = getAttribute(root, path, name);
+        }
+        if (className != null && className.endsWith(".class")) {
+            int i = className.lastIndexOf(".class");
+            String cname = className.substring(0, i);
+            return cname;
+        }
+        return className;
+    }
+
+    /** Returns the value of a given attribute, or null if not set */
+    public static String getRequiredAttribute(String source, XMLNode root,
+            String path, String name) throws MissingFieldException, BadFieldException {
+        String s = getAttribute(root, path, name, null);
+        if (s == null) {
+            throw new MissingFieldException(source, getPathString(root) + path + name);
+        }
+        s = s.trim();
+        return (s.length() == 0) ? null : s;
+    }
+
+    /** Returns the value of a given attribute, or the default value 'def' if not set */
+    public static String getAttribute(XMLNode root, String path, String name,
+            String def) throws BadFieldException {
+        XMLNode elem = findElementPath(root, path);
+        if (elem == null) {
+            return def;
+        }
+        String value = elem.getAttribute(name);
+        return (value == null || value.length() == 0) ? def : value;
+    }
+
+    /** Expands a URL into an absolute URL from a relative URL */
+    public static URL getAttributeURL(String source, URL base, XMLNode root, String path, String name) throws BadFieldException {
+        String value = getAttribute(root, path, name);
+        if (value == null) return null;
+        try {
+            if (value.startsWith("jar:")) {
+                int bang = value.indexOf("!/");
+                if (bang > 0) {
+                    String entry = value.substring(bang);
+                    String urlString = value.substring(4, bang);
+                    URL url = (base == null) ?
+                        new URL(urlString) : new URL(base, urlString);
+                    return new URL("jar:" + url.toString() + entry);
+                }
+            }
+            return (base == null) ? new URL(value) : new URL(base, value);
+        } catch(MalformedURLException mue) {
+            if (mue.getMessage().contains("https")) {
+                throw new BadFieldException(source, "<jnlp>", "https");
+            }
+            throw new BadFieldException(source, getPathString(root) + path + name, value);
+        }
+    }
+
+    /** Returns the value of an attribute as a URL or null if not set */
+    public static URL getAttributeURL(String source, XMLNode root, String path, String name) throws BadFieldException {
+        return getAttributeURL(source, null, root, path, name);
+    }
+
+    public static URL getRequiredURL(String source, URL base, XMLNode root, String path, String name) throws BadFieldException, MissingFieldException {
+        URL url = getAttributeURL(source, base, root, path, name);
+        if (url == null) {
+            throw new MissingFieldException(source, getPathString(root) + path + name);
+        }
+        return url;
+    }
+
+    /** Returns the value of an attribute as a URL. Throws a MissingFieldException if the
+     *  attribute is not defined
+     */
+    public static URL getRequiredURL(String source, XMLNode root, String path, String name) throws BadFieldException, MissingFieldException {
+        return getRequiredURL(source, null, root, path, name);
+    }
+
+    /** Returns true if the path exists in the document, otherwise false */
+    public static boolean isElementPath(XMLNode root, String path) {
+        return findElementPath(root, path) != null;
+    }
+
+    public static URL getElementURL(String source, XMLNode root, String path) throws BadFieldException {
+        String value = getElementContents(root, path);
+        try {
+            return new URL(value);
+        } catch(MalformedURLException mue) {
+            throw new BadFieldException(source, getPathString(root) + path, value);
+        }
+    }
+
+    /** Returns a string describing the current location in the DOM */
+    public static String getPathString(XMLNode e) {
+        return (e == null || !(e.isElement())) ? "" : getPathString(e.getParent()) + "<" + e.getName() + ">";
+    }
+
+    /** Returns the contents of an element with the given path and an attribute matching a specific value. Returns
+     *  NULL if not found
+     */
+    public static String getElementContentsWithAttribute(XMLNode root, String path, String attr, String val, String defaultvalue)
+            throws BadFieldException, MissingFieldException {
+        XMLNode e = getElementWithAttribute(root, path, attr, val);
+        if (e == null) {
+            return defaultvalue;
+        }
+        return getElementContents(e, "", defaultvalue);
+    }
+
+    public static URL getAttributeURLWithAttribute(String source, XMLNode root, String path, String attrcond, String val,
+            String name, URL defaultvalue)
+            throws BadFieldException, MissingFieldException {
+        XMLNode e = getElementWithAttribute(root, path, attrcond, val);
+        if (e == null) {
+            return defaultvalue;
+        }
+        URL url = getAttributeURL(source, e, "", name);
+        if (url == null) {
+            return defaultvalue;
+        }
+        return url;
+    }
+
+    /** Returns an element with the given path and an attribute matching a specific value. Returns
+     *  NULL if not found
+     */
+    public static XMLNode getElementWithAttribute(XMLNode root, String path, final String attr, final String val)
+            throws BadFieldException, MissingFieldException {
+        final XMLNode[] result = {null};
+        visitElements(root, path, new ElementVisitor() {
+            public void visitElement(XMLNode e) throws BadFieldException, MissingFieldException {
+                if (result[0] == null && e.getAttribute(attr).equals(val)) {
+                    result[0] = e;
+                }
+            }
+        });
+        return result[0];
+    }
+
+    /** Like getElementContents(...) but with a defaultValue of null */
+    public static String getElementContents(XMLNode root, String path) {
+        return getElementContents(root, path, null);
+    }
+
+    /** Returns the value of the last element tag in the path, e.g.,  <..><tag>value</tag>. The DOM is assumes
+     *  to be normalized. If no value is found, the defaultvalue is returned
+     */
+    public static String getElementContents(XMLNode root, String path, String defaultvalue) {
+        XMLNode e = findElementPath(root, path);
+        if (e == null) {
+            return defaultvalue;
+        }
+        XMLNode n = e.getNested();
+        if (n != null && !n.isElement()) {
+            return n.getName();
+        }
+        return defaultvalue;
+    }
+
+    /** Parses a path string of the form <tag1><tag2><tag3> and returns the specific Element
+     *  node for that tag, or null if it does not exist. If multiple elements exists with same
+     *  path the first is returned
+     */
+    public static XMLNode findElementPath(XMLNode elem, String path) {
+        // End condition. Root null -> path does not exist
+        if (elem == null) {
+            return null;
+        }
+
+        // End condition. String empty, return current root
+        if (path == null || path.length() == 0) {
+            return elem;
+        }
+
+        // Strip of first tag
+        int idx = path.indexOf('>');
+        if (!(path.charAt(0) == '<')) {
+            throw new IllegalArgumentException("bad path. Missing begin tag");
+        }
+        if (idx == -1) {
+            throw new IllegalArgumentException("bad path. Missing end tag");
+        }
+        String head = path.substring(1, idx);
+        String tail = path.substring(idx + 1);
+        return findElementPath(findChildElement(elem, head), tail);
+    }
+
+    /** Returns an child element with the current tag name or null. */
+    public static XMLNode findChildElement(XMLNode elem, String tag) {
+        XMLNode n = elem.getNested();
+        while (n != null) {
+            if (n.isElement() && n.getName().equals(tag)) {
+                return n;
+            }
+            n = n.getNext();
+        }
+        return null;
+    }
+
+    /** Iterator class */
+    public abstract static class ElementVisitor {
+        abstract public void visitElement(XMLNode e) throws BadFieldException, MissingFieldException;
+    }
+
+    /** Visits all elements which matches the <path>. The iteration is only
+     *  done on the last element in the path.
+     */
+    public static void visitElements(XMLNode root, String path, ElementVisitor ev)
+            throws BadFieldException, MissingFieldException {
+        // Get last element in path
+        int idx = path.lastIndexOf('<');
+        if (idx == -1) {
+            throw new IllegalArgumentException(
+                    "bad path. Must contain atleast one tag");
+        }
+        if (path.length() == 0 || path.charAt(path.length() - 1) != '>') {
+            throw new IllegalArgumentException("bad path. Must end with a >");
+        }
+        String head = path.substring(0, idx);
+        String tag = path.substring(idx + 1, path.length() - 1);
+
+        XMLNode elem = findElementPath(root, head);
+        if (elem == null) {
+            return;
+        }
+
+        // Iterate through all child nodes
+        XMLNode n = elem.getNested();
+        while (n != null) {
+            if (n.isElement() && n.getName().equals(tag)) {
+                ev.visitElement(n);
+            }
+            n = n.getNext();
+        }
+    }
+
+    public static void visitChildrenElements(XMLNode elem, ElementVisitor ev)
+            throws BadFieldException, MissingFieldException {
+        // Iterate through all child nodes
+        XMLNode n = elem.getNested();
+        while (n != null) {
+            if (n.isElement()) {
+                ev.visitElement(n);
+            }
+            n = n.getNext();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/exception/BadFieldException.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2006, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jnlp.converter.parser.exception;
+
+public class BadFieldException extends Exception {
+
+    private final String _field;
+    private final String _value;
+
+    public BadFieldException(String source, String field, String value) {
+        super();
+        _value = value;
+        _field = field;
+    }
+
+    /**
+     * Returns the name of the offending field
+     */
+    public String getField() {
+        return _field;
+    }
+
+    /**
+     * Returns the value of the offending field
+     */
+    public String getValue() {
+        return _value;
+    }
+
+    /**
+     * toString implementation
+     */
+    @Override
+    public String toString() {
+        return "BadFieldException[ " + getField() + "," + getValue() + "]";
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/exception/JNLParseException.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2006, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jnlp.converter.parser.exception;
+
+/**
+ * Exception thrown if a parse error occurred when interpreting
+ * the launch descriptor
+ */
+
+public class JNLParseException extends Exception {
+
+    private final String _msg;
+    private final int _line;
+
+    public JNLParseException(Exception exception, String msg, int line) {
+        super(exception);
+        _msg = msg;
+        _line = line;
+    }
+
+    @Override
+    public String getMessage() {
+        return _msg;
+    }
+
+    private int getLine() {
+        return _line;
+    }
+
+    @Override
+    public String toString() {
+        return "JNLParseException[ " + getMessage() + "] at " + getLine();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/exception/MissingFieldException.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2006, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jnlp.converter.parser.exception;
+
+public class MissingFieldException extends Exception {
+
+    private final String _field;
+
+    public MissingFieldException(String source, String field) {
+        super();
+        _field = field;
+    }
+
+    /**
+     * Returns the name of the offending field
+     */
+    private String getField() {
+        return _field;
+    }
+
+    /**
+     * toString implementation
+     */
+    @Override
+    public String toString() {
+        return "MissingFieldException[ " + getField() + "]";
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/xml/XMLAttribute.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2006, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jnlp.converter.parser.xml;
+
+/**
+ * Class that contains information about a specific attribute
+ */
+public class XMLAttribute {
+
+    private final String _name;
+    private final String _value;
+    private XMLAttribute _next;
+
+    public XMLAttribute(String name, String value) {
+        _name = XMLNode.stripNameSpace(name);
+        _value = value;
+    }
+
+    public String getName() {
+        return _name;
+    }
+
+    public String getValue() {
+        return _value;
+    }
+
+    public XMLAttribute getNext() {
+        return _next;
+    }
+
+    public void setNext(XMLAttribute next) {
+        _next = next;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/xml/XMLEncoding.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,342 @@
+/*
+ * Copyright (c) 2006, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jnlp.converter.parser.xml;
+
+import java.io.ByteArrayInputStream;
+import java.io.EOFException;
+import java.io.InputStreamReader;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.UnsupportedEncodingException;
+
+public class XMLEncoding {
+    /**
+     * Decodes a byte stream into a String by testing for a Byte Order Mark
+     * (BOM) or an XML declaration.
+     * <br />
+     * Detection begins by examining the first four octets of the stream for a
+     * BOM. If a BOM is not found, then an encoding declaration is looked for
+     * at the beginning of the stream. If the encoding still can not be
+     * determined at this point, then UTF-8 is assumed.
+     *
+     * @param data  an array of bytes containing an encoded XML document.
+     *
+     * @return A string containing the decoded XML document.
+     */
+    public static String decodeXML(byte [] data) throws IOException {
+        int start = 0;
+        String encoding;
+
+        if (data.length < BOM_LENGTH) {
+            throw (new EOFException("encoding.error.not.xml"));
+        }
+        // no else required; successfully read stream
+        int firstFour = ((0xff000000 & ((int) data[0] << 24)) |
+                         (0x00ff0000 & ((int) data[1] << 16)) |
+                         (0x0000ff00 & ((int) data[2] <<  8)) |
+                         (0x000000ff &  (int) data[3]));
+
+        // start by examining the first four bytes for a BOM
+        switch (firstFour) {
+            case EBCDIC:
+                // examine the encoding declaration
+                encoding = examineEncodingDeclaration(data, IBM037_ENC);
+                break;
+
+            case XML_DECLARATION:
+                // assume UTF-8, but examine the encoding declaration
+                encoding = examineEncodingDeclaration(data, UTF_8_ENC);
+                break;
+
+            case UTF_16BE:
+                encoding = UTF_16BE_ENC;
+                break;
+
+            case UTF_16LE:
+                encoding = UTF_16LE_ENC;
+                break;
+
+            case UNUSUAL_OCTET_1:
+            case UNUSUAL_OCTET_2:
+                throw (new UnsupportedEncodingException("encoding.error.unusual.octet"));
+
+            case UTF_32_BE_BOM:
+            case UTF_32_LE_BOM:
+                encoding = UTF_32_ENC;
+                break;
+
+            default:
+                int firstThree = firstFour & 0xffffff00;
+
+                switch (firstThree) {
+                    case UTF_8_BOM:
+                        // the InputStreamReader class doen't properly handle
+                        // the Byte Order Mark (BOM) in UTF-8 streams, so don't
+                        // putback those 3 bytes.
+                        start    = 3;
+                        encoding = UTF_8_ENC;
+                        break;
+
+                    default:
+                        int firstTwo = firstFour & 0xffff0000;
+
+                        switch (firstTwo) {
+                            case UTF_16_BE_BOM:
+                            case UTF_16_LE_BOM:
+                                encoding = UTF_16_ENC;
+                                break;
+
+                            default:
+                                // this is probably UTF-8 without the encoding
+                                // declaration
+                                encoding = UTF_8_ENC;
+                                break;
+                        }
+                        break;
+                }
+                break;
+        }
+
+        return (new String(data, start, data.length - start, encoding));
+    }
+
+    /**
+     * [3]  S            ::= ( #x20 | #x09 | #x0d | #x0a )
+     * [23] XMLDecl      ::= '<?xml' VersionInfo EncodingDecl? SDDecl? S? '?>'
+     * [24] VersionInfo  ::= S 'version' Eq ( '"' VersionNum '"' |
+     *                                        "'" VersionNum "'" )
+     * [25] Eq           ::= S? '=' S?
+     * [26] VersionNum   ::= ([a-zA-Z0-9_.:] | '-')+
+     * [80] EncodingDecl ::= S 'encoding' Eq ( '"' EncName '"' |
+     *                                         "'" EncName "'" )
+     * [81] EncName      ::= [a-zA-Z] ([a-zA-Z0-9_.] | '-')*
+     */
+    private static String examineEncodingDeclaration(byte [] data,
+                          String    encoding) throws IOException {
+        boolean loop       = false;
+        boolean recognized = false;
+        boolean almost     = false;
+        boolean question   = false;
+        boolean done       = false;
+        boolean found      = false;
+        int     pos        = 0;
+        int     ch         = -1;
+        Reader  reader     = null;
+        String  result     = ((encoding != null) ? encoding : UTF_8_ENC);
+
+        reader = new InputStreamReader(new ByteArrayInputStream(data), result);
+        ch     = reader.read();
+
+        // if this is an XML declaration, it will start with the text '<?xml'
+        for (int i = 0; ((i < XML_DECL_START.length()) && (done == false)); i++) {
+            if (ch != XML_DECL_START.charAt(i)) {
+                // This doesn't look like an XML declaration.  This method
+                // should only be called if the stream contains an XML
+                // declaration in the encoding that is passed into the method.
+                done = true;
+                break;
+            }
+            // no else required; still matches
+            ch = reader.read();
+        }
+
+        // there must be at least one whitespace character next.
+        loop = true;
+        while ((loop == true) && (done == false)) {
+            switch (ch) {
+                case SPACE:
+                case TAB:         // intentional
+                case LINEFEED:    // fall
+                case RETURN:      // through
+                    ch = reader.read();
+                    break;
+
+                case -1:
+                    // unexpected EOF
+                    done = true;
+                    break;
+
+                default:
+                    // non-whitespace
+                    loop = false;
+                    break;
+            }
+        }
+
+        // now look for the text 'encoding', but if the end of the XML
+        // declaration (signified by the text '?>') comes first, then
+        // assume the encoding is UTF-8
+        loop = true;
+        while ((loop == true) && (done == false)) {
+            if (ch == -1) {
+                // unexpected EOF
+                done = true;
+                break;
+            } else if (recognized == true) {
+                // this is the encoding declaration as long as the next few
+                // characters are whitespace and/or the equals ('=') sign
+                switch (ch) {
+                    case SPACE:       // intentional
+                    case TAB:         // fall
+                    case LINEFEED:    // through
+                    case RETURN:
+                        // don't need to do anything
+                        break;
+
+                    case EQUAL:
+                        if (almost == false) {
+                            // got the equal, now find a quote
+                            almost = true;
+                        } else {
+                            // this is not valid XML, so punt
+                            recognized = false;
+                            done       = true;
+                        }
+                        break;
+
+                    case DOUBLE_QUOTE:    // intentional
+                    case SINGLE_QUOTE:    // fall through
+                        if (almost == true) {
+                            // got the quote, so move on to get the value
+                            loop = false;
+                        } else {
+                            // got a quote before the equal; this is not valid
+                            // XML, so punt
+                            recognized = false;
+                            done       = true;
+                        }
+                        break;
+
+                    default:
+                        // non-whitespace
+                        recognized = false;
+                        if (almost == true) {
+                            // this is not valid XML, so punt
+                            done = true;
+                        }
+                        // no else required; this wasn't the encoding
+                        // declaration
+                        break;
+                }
+
+                if (recognized == false) {
+                    // this isn't the encoding declaration, so go back to the
+                    // top without reading the next character
+                    pos = 0;
+                    continue;
+                }
+                // no else required; still looking good
+            } else if (ch == ENCODING_DECL.charAt(pos++)) {
+                if (ENCODING_DECL.length() == pos) {
+                    // this looks like the encoding declaration
+                    recognized = true;
+                }
+                // no else required; this might be the encoding declaration
+            } else if (ch == '?') {
+                question = true;
+                pos      = 0;
+            } else if ((ch == '>') && (question == true)) {
+                // there is no encoding declaration, so assume that the initial
+                // encoding guess was correct
+                done   = true;
+                continue;
+            } else {
+                // still searching for the encoding declaration
+                pos = 0;
+            }
+
+            ch = reader.read();
+        }
+
+        if (done == false) {
+            StringBuilder buffer = new StringBuilder(MAX_ENC_NAME);
+
+            if (((ch >= 'a') && (ch <= 'z')) |
+                ((ch >= 'A') && (ch <= 'Z'))) {
+                // add the character to the result
+                buffer.append((char) ch);
+
+                loop = true;
+                while ((loop == true) && (done == false)) {
+                    ch = reader.read();
+
+                    if (((ch >= 'a') && (ch <= 'z')) ||
+                        ((ch >= 'A') && (ch <= 'Z')) ||
+                        ((ch >= '0') && (ch <= '9')) ||
+                        (ch == '_') || (ch == '.') || (ch == '-')) {
+                        // add the character to the result
+                        buffer.append((char) ch);
+                    } else if ((ch == DOUBLE_QUOTE) || (ch == SINGLE_QUOTE)) {
+                        // finished!
+                        found  = true;
+                        done   = true;
+                        result = buffer.toString();
+                    } else {
+                        // this is not a valid encoding name, so punt
+                        done = true;
+                    }
+                }
+            } else {
+                // this is not a valid encoding name, so punt
+                done = true;
+            }
+        }
+        // no else required; already failed to find the encoding somewhere else
+
+        return (result);
+    }
+
+    private static final int BOM_LENGTH   = 4;
+    private static final int MAX_ENC_NAME = 512;
+
+    private static final int SPACE        = 0x00000020;
+    private static final int TAB          = 0x00000009;
+    private static final int LINEFEED     = 0x0000000a;
+    private static final int RETURN       = 0x0000000d;
+    private static final int EQUAL        = '=';
+    private static final int DOUBLE_QUOTE = '\"';
+    private static final int SINGLE_QUOTE = '\'';
+
+    private static final int UTF_32_BE_BOM   = 0x0000feff;
+    private static final int UTF_32_LE_BOM   = 0xfffe0000;
+    private static final int UTF_16_BE_BOM   = 0xfeff0000;
+    private static final int UTF_16_LE_BOM   = 0xfffe0000;
+    private static final int UTF_8_BOM       = 0xefbbbf00;
+    private static final int UNUSUAL_OCTET_1 = 0x00003c00;
+    private static final int UNUSUAL_OCTET_2 = 0x003c0000;
+    private static final int UTF_16BE        = 0x003c003f;
+    private static final int UTF_16LE        = 0x3c003f00;
+    private static final int EBCDIC          = 0x4c6fa794;
+    private static final int XML_DECLARATION = 0x3c3f786d;
+
+    private static final String UTF_32_ENC   = "UTF-32";
+    private static final String UTF_16_ENC   = "UTF-16";
+    private static final String UTF_16BE_ENC = "UTF-16BE";
+    private static final String UTF_16LE_ENC = "UTF-16LE";
+    private static final String UTF_8_ENC    = "UTF-8";
+    private static final String IBM037_ENC   = "IBM037";
+
+    private static final String XML_DECL_START = "<?xml";
+    private static final String ENCODING_DECL  = "encoding";
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/xml/XMLNode.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,201 @@
+/*
+ * Copyright (c) 2006, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jnlp.converter.parser.xml;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+/**
+ * Class that contains information about an XML Node
+ */
+public class XMLNode {
+    private final boolean _isElement;     // Element/PCTEXT
+    private final String _name;
+    private final XMLAttribute _attr;
+    private XMLNode _parent;  // Parent Node
+    private XMLNode _nested;  // Nested XML tags
+    private XMLNode _next;    // Following XML tag on the same level
+
+    public final static String WILDCARD = "*";
+
+    /** Creates a PCTEXT node */
+    public XMLNode(String name) {
+        _isElement = false;
+        _name = name;
+        _attr = null;
+        _nested = null;
+        _next = null;
+        _parent = null;
+    }
+
+    /** Creates a ELEMENT node */
+    public XMLNode(String name, XMLAttribute attr) {
+        _isElement = true;
+        _name = stripNameSpace(name);
+        _attr = attr;
+        _nested = null;
+        _next = null;
+        _parent = null;
+    }
+
+    public String getName() {
+        return _name;
+    }
+
+    public XMLAttribute getAttributes() {
+        return _attr;
+    }
+
+    public XMLNode getNested() {
+        return _nested;
+    }
+
+    public XMLNode getNext() {
+        return _next;
+    }
+
+    public boolean isElement() {
+        return _isElement;
+    }
+
+    public void setParent(XMLNode parent) {
+        _parent = parent;
+    }
+
+    public XMLNode getParent() {
+        return _parent;
+    }
+
+    public void setNext(XMLNode next) {
+        _next = next;
+    }
+
+    public void setNested(XMLNode nested) {
+        _nested = nested;
+    }
+
+    public static String stripNameSpace(String name) {
+        if (name != null && !name.startsWith("xmlns:")) {
+            int i = name.lastIndexOf(":");
+            if (i >= 0 && i < name.length()) {
+                return name.substring(i+1);
+            }
+        }
+        return name;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 3;
+        hash = 83 * hash + (this._name != null ? this._name.hashCode() : 0);
+        hash = 83 * hash + (this._attr != null ? this._attr.hashCode() : 0);
+        hash = 83 * hash + (this._nested != null ? this._nested.hashCode() : 0);
+        hash = 83 * hash + (this._next != null ? this._next.hashCode() : 0);
+        return hash;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == null || !(o instanceof XMLNode)) return false;
+        XMLNode other = (XMLNode)o;
+        boolean result =
+            match(_name, other._name) &&
+            match(_attr, other._attr) &&
+            match(_nested, other._nested) &&
+            match(_next, other._next);
+        return result;
+    }
+
+    public String getAttribute(String name) {
+        XMLAttribute cur = _attr;
+        while(cur != null) {
+            if (name.equals(cur.getName())) return cur.getValue();
+            cur = cur.getNext();
+        }
+        return "";
+    }
+
+    private static boolean match(Object o1, Object o2) {
+        if (o1 == null) {
+            return (o2 == null);
+        }
+        return o1.equals(o2);
+    }
+
+    public void printToStream(PrintWriter out) {
+        printToStream(out, false);
+    }
+
+    public void printToStream(PrintWriter out, boolean trim) {
+        printToStream(out, 0, trim);
+    }
+
+    public void printToStream(PrintWriter out, int n, boolean trim) {
+        if (!isElement()) {
+            String value = _name; // value node (where name is data of parent)
+            if (trim && value.length() > 512) {
+                value = "...";
+            }
+            out.print(value);
+        } else {
+            if (_nested == null) {
+                String attrString = (_attr == null) ? "" : (" " + _attr.toString());
+                lineln(out, n, "<" + _name + attrString + "/>");
+            } else {
+                String attrString = (_attr == null) ? "" : (" " + _attr.toString());
+                lineln(out, n, "<" + _name + attrString + ">");
+                _nested.printToStream(out, n + 1, trim);
+                if (_nested.isElement()) {
+                    lineln(out, n, "</" + _name + ">");
+                } else {
+                    out.print("</" + _name + ">");
+                }
+            }
+        }
+        if (_next != null) {
+            _next.printToStream(out, n, trim);
+        }
+    }
+
+    private static void lineln(PrintWriter out, int indent, String s) {
+        out.println("");
+        for(int i = 0; i < indent; i++) {
+            out.print("  ");
+        }
+        out.print(s);
+    }
+
+    @Override
+    public String toString() {
+        return toString(false);
+    }
+
+    public String toString(boolean hideLongElementValue) {
+        StringWriter sw = new StringWriter(1000);
+        PrintWriter pw = new PrintWriter(sw);
+        printToStream(pw, hideLongElementValue);
+        pw.close();
+        return sw.toString();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/jpackage/JNLPConverter/src/jnlp/converter/parser/xml/XMLParser.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2014, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jnlp.converter.parser.xml;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.xml.sax.InputSource;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+import java.io.StringReader;
+import java.io.IOException;
+import java.util.Stack;
+
+public class XMLParser extends DefaultHandler {
+
+    private XMLNode _root;
+    private final String _source;
+    private Stack<XMLNode> _inProgress;
+    private String _characters;
+
+    // although defined in com.sun.org.apache.xerces.internal.impl.Constants,
+    // we should not be able to access that, so defined here
+    private final static String DTD_DOWNLOAD =
+        "http://apache.org/xml/features/nonvalidating/load-external-dtd";
+
+    private static final SAXParserFactory SPF = SAXParserFactory.newInstance();
+
+    /*
+     * Construct an <code>XMLParser</code>.
+     *
+     * @param source  - the source text to parse.
+     */
+    public XMLParser(String source) {
+        _source = source.trim();
+    }
+
+    public XMLNode parse() throws SAXException {
+        // normally we parse without validating, but leave option to parse
+        // with validation, possibly controlled by config option.
+        return parse(false);
+    }
+
+    public XMLNode parse(boolean validating) throws SAXException {
+        _root = null;
+        _inProgress = new Stack<>();
+
+        try {
+            InputSource is = new InputSource(new StringReader(_source));
+            SPF.setValidating(validating);
+            // only download dtd file from DOCTYPE if we are doing validation
+            try {
+                SPF.setFeature(DTD_DOWNLOAD, validating);
+            } catch (Exception e) {
+            }
+            SAXParser sp = SPF.newSAXParser();
+            sp.parse(is, this);
+        } catch (ParserConfigurationException | IOException pce) {
+            throw new SAXException(pce);
+        }
+
+        return _root;
+    }
+
+    @Override
+    public void startElement(String uri, String localeName, String qName,
+            Attributes attributes) throws SAXException {
+
+        XMLAttribute first = null;
+        XMLAttribute last = null;
+        int len = attributes.getLength();
+
+        for (int i = 0; i < len; i++) {
+            XMLAttribute att = new XMLAttribute(
+                    // in old implementation attribute names and values were trimmed
+                    attributes.getQName(i).trim(), attributes.getValue(i).trim());
+            if (first == null) {
+                first = att;
+            }
+            if (last != null) {
+                last.setNext(att);
+            }
+            last = att;
+        }
+        _inProgress.push(new XMLNode(qName, first));
+        _characters = null;
+    }
+
+    @Override
+    public void endElement(String uri, String localeName, String elementName)
+            throws SAXException {
+        XMLNode node = _inProgress.pop();
+        // <information>
+        //  <title>Title</title>
+        //  <vendor>Vendor</vendor> "Some whitespaces"
+        // </information>
+        // In example above when we receive end of <information> we will
+        // have _characters set to whitespace and new line and it will be
+        // added as child node to <information>. This will break our cache code
+        // which will think that JNLP file changed on server even if it is not
+        // and thus we might not load properly.
+        //
+        // <application-desc name="HelloWorld" main-class="HelloWorld">
+        //  <argument>  test with whitespaces </argument>
+        // </application-desc>
+        // From example above we want to include whitespaces for <argument>.
+        //
+        // <node1>
+        //  <node2>abc</node2>
+        //  xyz (might be whitespaces)
+        // </node1>
+        // In JNLP spec we do not have cases when node have nested nodes as
+        // well as text which is whitespaces only.
+        //
+        // So to fix it lets check if ending node have nested nodes, then do
+        // not add whitespaces only node.
+        if (node != null && node.getNested() != null && _characters != null) {
+            String trimCharacters = _characters.trim();
+            if ((trimCharacters == null) || (trimCharacters.length() == 0)) {
+                _characters = null; // No need to add whitespaces only
+            }
+        }
+        if ((_characters != null) && (_characters.trim().length() > 0)) {
+            addChild(node, new XMLNode(_characters));
+        }
+
+        if (_inProgress.isEmpty()) {
+            _root = node;
+        } else {
+            addChild(_inProgress.peek(), node);
+        }
+        _characters = null;
+    }
+
+    @Override
+    public void ignorableWhitespace(char[] chars, int start, int length)
+            throws SAXException {
+        String s = new String(chars, start, length);
+        _characters = ((_characters == null) ? s : _characters + s);
+    }
+
+    private void addChild(XMLNode parent, XMLNode child) {
+        child.setParent(parent);
+
+        XMLNode sibling = parent.getNested();
+        if (sibling == null) {
+            parent.setNested(child); // set us as only child
+        } else {
+            while (sibling.getNext() != null) {
+                sibling = sibling.getNext();
+            }
+            sibling.setNext(child); // sets us as youngest child
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/nbproject/jpackage/JNLPConverter/build.xml	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- You may freely edit this file. See commented blocks below for -->
+<!-- some examples of how to customize the build. -->
+<!-- (If you delete it and reopen the project it will be recreated.) -->
+<!-- By default, only the Clean and Build commands use this build script. -->
+<!-- Commands such as Run, Debug, and Test only use this build script if -->
+<!-- the Compile on Save feature is turned off for the project. -->
+<!-- You can turn off the Compile on Save (or Deploy on Save) setting -->
+<!-- in the project's Project Properties dialog box.-->
+<project name="JNLPConverter" default="default" basedir=".">
+    <description>Builds, tests, and runs the project JNLPConverter.</description>
+    <import file="nbproject/build-impl.xml"/>
+    <!--
+
+    There exist several targets which are by default empty and which can be
+    used for execution of your tasks. These targets are usually executed
+    before and after some main targets. They are:
+
+      -pre-init:                 called before initialization of project properties
+      -post-init:                called after initialization of project properties
+      -pre-compile:              called before javac compilation
+      -post-compile:             called after javac compilation
+      -pre-compile-single:       called before javac compilation of single file
+      -post-compile-single:      called after javac compilation of single file
+      -pre-compile-test:         called before javac compilation of JUnit tests
+      -post-compile-test:        called after javac compilation of JUnit tests
+      -pre-compile-test-single:  called before javac compilation of single JUnit test
+      -post-compile-test-single: called after javac compilation of single JUunit test
+      -pre-jar:                  called before JAR building
+      -post-jar:                 called after JAR building
+      -post-clean:               called after cleaning build products
+
+    (Targets beginning with '-' are not intended to be called on their own.)
+
+    Example of inserting an obfuscator after compilation could look like this:
+
+        <target name="-post-compile">
+            <obfuscate>
+                <fileset dir="${build.classes.dir}"/>
+            </obfuscate>
+        </target>
+
+    For list of available properties check the imported
+    nbproject/build-impl.xml file.
+
+
+    Another way to customize the build is by overriding existing main targets.
+    The targets of interest are:
+
+      -init-macrodef-javac:     defines macro for javac compilation
+      -init-macrodef-junit:     defines macro for junit execution
+      -init-macrodef-debug:     defines macro for class debugging
+      -init-macrodef-java:      defines macro for class execution
+      -do-jar:                  JAR building
+      run:                      execution of project
+      -javadoc-build:           Javadoc generation
+      test-report:              JUnit report generation
+
+    An example of overriding the target for project execution could look like this:
+
+        <target name="run" depends="JNLPConverter-impl.jar">
+            <exec dir="bin" executable="launcher.exe">
+                <arg file="${dist.jar}"/>
+            </exec>
+        </target>
+
+    Notice that the overridden target depends on the jar target and not only on
+    the compile target as the regular run target does. Again, for a list of available
+    properties which you can use, check the target you are overriding in the
+    nbproject/build-impl.xml file.
+
+    -->
+    <target name="-post-jar">
+        <copy file="${dist.jar}" todir="../../../jpackage/jnlpconverter"/>
+    </target>
+
+    <target name="-post-clean">
+        <delete file="../../../jpackage/jnlpconverter/JNLPConverter.jar"/>
+    </target>
+</project>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/nbproject/jpackage/JNLPConverter/manifest.mf	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+X-COMMENT: Main-Class will be added automatically by build
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/nbproject/jpackage/JNLPConverter/nbproject/build-impl.xml	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,1403 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+*** GENERATED FROM project.xml - DO NOT EDIT  ***
+***         EDIT ../build.xml INSTEAD         ***
+
+For the purpose of easier reading the script
+is divided into following sections:
+
+  - initialization
+  - compilation
+  - jar
+  - execution
+  - debugging
+  - javadoc
+  - test compilation
+  - test execution
+  - test debugging
+  - applet
+  - cleanup
+
+        -->
+<project xmlns:j2seproject1="http://www.netbeans.org/ns/j2se-project/1" xmlns:j2seproject3="http://www.netbeans.org/ns/j2se-project/3" xmlns:jaxrpc="http://www.netbeans.org/ns/j2se-project/jax-rpc" basedir=".." default="default" name="JNLPConverter-impl">
+    <fail message="Please build using Ant 1.8.0 or higher.">
+        <condition>
+            <not>
+                <antversion atleast="1.8.0"/>
+            </not>
+        </condition>
+    </fail>
+    <target depends="test,jar,javadoc" description="Build and test whole project." name="default"/>
+    <!--
+                ======================
+                INITIALIZATION SECTION
+                ======================
+            -->
+    <target name="-pre-init">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target depends="-pre-init" name="-init-private">
+        <property file="nbproject/private/config.properties"/>
+        <property file="nbproject/private/configs/${config}.properties"/>
+        <property file="nbproject/private/private.properties"/>
+    </target>
+    <target depends="-pre-init,-init-private" name="-init-user">
+        <property file="${user.properties.file}"/>
+        <!-- The two properties below are usually overridden -->
+        <!-- by the active platform. Just a fallback. -->
+        <property name="default.javac.source" value="1.6"/>
+        <property name="default.javac.target" value="1.6"/>
+    </target>
+    <target depends="-pre-init,-init-private,-init-user" name="-init-project">
+        <property file="nbproject/configs/${config}.properties"/>
+        <property file="nbproject/project.properties"/>
+    </target>
+    <target depends="-pre-init,-init-private,-init-user,-init-project,-init-macrodef-property" name="-do-init">
+        <property name="platform.java" value="${java.home}/bin/java"/>
+        <available file="${manifest.file}" property="manifest.available"/>
+        <condition property="splashscreen.available">
+            <and>
+                <not>
+                    <equals arg1="${application.splash}" arg2="" trim="true"/>
+                </not>
+                <available file="${application.splash}"/>
+            </and>
+        </condition>
+        <condition property="main.class.available">
+            <and>
+                <isset property="main.class"/>
+                <not>
+                    <equals arg1="${main.class}" arg2="" trim="true"/>
+                </not>
+            </and>
+        </condition>
+        <condition property="profile.available">
+            <and>
+                <isset property="javac.profile"/>
+                <length length="0" string="${javac.profile}" when="greater"/>
+                <matches pattern="((1\.[89])|9)(\..*)?" string="${javac.source}"/>
+            </and>
+        </condition>
+        <condition property="do.archive">
+            <or>
+                <not>
+                    <istrue value="${jar.archive.disabled}"/>
+                </not>
+                <istrue value="${not.archive.disabled}"/>
+            </or>
+        </condition>
+        <condition property="do.mkdist">
+            <and>
+                <isset property="do.archive"/>
+                <isset property="libs.CopyLibs.classpath"/>
+                <not>
+                    <istrue value="${mkdist.disabled}"/>
+                </not>
+            </and>
+        </condition>
+        <condition property="do.archive+manifest.available">
+            <and>
+                <isset property="manifest.available"/>
+                <istrue value="${do.archive}"/>
+            </and>
+        </condition>
+        <condition property="do.archive+main.class.available">
+            <and>
+                <isset property="main.class.available"/>
+                <istrue value="${do.archive}"/>
+            </and>
+        </condition>
+        <condition property="do.archive+splashscreen.available">
+            <and>
+                <isset property="splashscreen.available"/>
+                <istrue value="${do.archive}"/>
+            </and>
+        </condition>
+        <condition property="do.archive+profile.available">
+            <and>
+                <isset property="profile.available"/>
+                <istrue value="${do.archive}"/>
+            </and>
+        </condition>
+        <condition property="have.tests">
+            <or/>
+        </condition>
+        <condition property="have.sources">
+            <or>
+                <available file="${src.src.dir}"/>
+            </or>
+        </condition>
+        <condition property="netbeans.home+have.tests">
+            <and>
+                <isset property="netbeans.home"/>
+                <isset property="have.tests"/>
+            </and>
+        </condition>
+        <condition property="no.javadoc.preview">
+            <and>
+                <isset property="javadoc.preview"/>
+                <isfalse value="${javadoc.preview}"/>
+            </and>
+        </condition>
+        <property name="run.jvmargs" value=""/>
+        <property name="run.jvmargs.ide" value=""/>
+        <property name="javac.compilerargs" value=""/>
+        <property name="work.dir" value="${basedir}"/>
+        <condition property="no.deps">
+            <and>
+                <istrue value="${no.dependencies}"/>
+            </and>
+        </condition>
+        <property name="javac.debug" value="true"/>
+        <property name="javadoc.preview" value="true"/>
+        <property name="application.args" value=""/>
+        <property name="source.encoding" value="${file.encoding}"/>
+        <property name="runtime.encoding" value="${source.encoding}"/>
+        <property name="manifest.encoding" value="${source.encoding}"/>
+        <condition property="javadoc.encoding.used" value="${javadoc.encoding}">
+            <and>
+                <isset property="javadoc.encoding"/>
+                <not>
+                    <equals arg1="${javadoc.encoding}" arg2=""/>
+                </not>
+            </and>
+        </condition>
+        <property name="javadoc.encoding.used" value="${source.encoding}"/>
+        <property name="includes" value="**"/>
+        <property name="excludes" value=""/>
+        <property name="do.depend" value="false"/>
+        <condition property="do.depend.true">
+            <istrue value="${do.depend}"/>
+        </condition>
+        <path id="endorsed.classpath.path" path="${endorsed.classpath}"/>
+        <condition else="" property="endorsed.classpath.cmd.line.arg" value="-Xbootclasspath/p:'${toString:endorsed.classpath.path}'">
+            <and>
+                <isset property="endorsed.classpath"/>
+                <not>
+                    <equals arg1="${endorsed.classpath}" arg2="" trim="true"/>
+                </not>
+            </and>
+        </condition>
+        <condition else="" property="javac.profile.cmd.line.arg" value="-profile ${javac.profile}">
+            <isset property="profile.available"/>
+        </condition>
+        <condition else="false" property="jdkBug6558476">
+            <and>
+                <matches pattern="1\.[56]" string="${java.specification.version}"/>
+                <not>
+                    <os family="unix"/>
+                </not>
+            </and>
+        </condition>
+        <condition else="false" property="javac.fork">
+            <or>
+                <istrue value="${jdkBug6558476}"/>
+                <istrue value="${javac.external.vm}"/>
+            </or>
+        </condition>
+        <property name="jar.index" value="false"/>
+        <property name="jar.index.metainf" value="${jar.index}"/>
+        <property name="copylibs.rebase" value="true"/>
+        <available file="${meta.inf.dir}/persistence.xml" property="has.persistence.xml"/>
+        <condition property="junit.available">
+            <or>
+                <available classname="org.junit.Test" classpath="${run.test.classpath}"/>
+                <available classname="junit.framework.Test" classpath="${run.test.classpath}"/>
+            </or>
+        </condition>
+        <condition property="testng.available">
+            <available classname="org.testng.annotations.Test" classpath="${run.test.classpath}"/>
+        </condition>
+        <condition property="junit+testng.available">
+            <and>
+                <istrue value="${junit.available}"/>
+                <istrue value="${testng.available}"/>
+            </and>
+        </condition>
+        <condition else="testng" property="testng.mode" value="mixed">
+            <istrue value="${junit+testng.available}"/>
+        </condition>
+        <condition else="" property="testng.debug.mode" value="-mixed">
+            <istrue value="${junit+testng.available}"/>
+        </condition>
+        <property name="java.failonerror" value="true"/>
+    </target>
+    <target name="-post-init">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target depends="-pre-init,-init-private,-init-user,-init-project,-do-init" name="-init-check">
+        <fail unless="src.src.dir">Must set src.src.dir</fail>
+        <fail unless="build.dir">Must set build.dir</fail>
+        <fail unless="dist.dir">Must set dist.dir</fail>
+        <fail unless="build.classes.dir">Must set build.classes.dir</fail>
+        <fail unless="dist.javadoc.dir">Must set dist.javadoc.dir</fail>
+        <fail unless="build.test.classes.dir">Must set build.test.classes.dir</fail>
+        <fail unless="build.test.results.dir">Must set build.test.results.dir</fail>
+        <fail unless="build.classes.excludes">Must set build.classes.excludes</fail>
+        <fail unless="dist.jar">Must set dist.jar</fail>
+    </target>
+    <target name="-init-macrodef-property">
+        <macrodef name="property" uri="http://www.netbeans.org/ns/j2se-project/1">
+            <attribute name="name"/>
+            <attribute name="value"/>
+            <sequential>
+                <property name="@{name}" value="${@{value}}"/>
+            </sequential>
+        </macrodef>
+    </target>
+    <target depends="-init-ap-cmdline-properties" if="ap.supported.internal" name="-init-macrodef-javac-with-processors">
+        <macrodef name="javac" uri="http://www.netbeans.org/ns/j2se-project/3">
+            <attribute default="${src.src.dir}" name="srcdir"/>
+            <attribute default="${build.classes.dir}" name="destdir"/>
+            <attribute default="${javac.classpath}" name="classpath"/>
+            <attribute default="${javac.processorpath}" name="processorpath"/>
+            <attribute default="${build.generated.sources.dir}/ap-source-output" name="apgeneratedsrcdir"/>
+            <attribute default="${includes}" name="includes"/>
+            <attribute default="${excludes}" name="excludes"/>
+            <attribute default="${javac.debug}" name="debug"/>
+            <attribute default="${empty.dir}" name="sourcepath"/>
+            <attribute default="${empty.dir}" name="gensrcdir"/>
+            <element name="customize" optional="true"/>
+            <sequential>
+                <property location="${build.dir}/empty" name="empty.dir"/>
+                <mkdir dir="${empty.dir}"/>
+                <mkdir dir="@{apgeneratedsrcdir}"/>
+                <javac debug="@{debug}" deprecation="${javac.deprecation}" destdir="@{destdir}" encoding="${source.encoding}" excludes="@{excludes}" fork="${javac.fork}" includeantruntime="false" includes="@{includes}" source="${javac.source}" sourcepath="@{sourcepath}" srcdir="@{srcdir}" target="${javac.target}" tempdir="${java.io.tmpdir}">
+                    <src>
+                        <dirset dir="@{gensrcdir}" erroronmissingdir="false">
+                            <include name="*"/>
+                        </dirset>
+                    </src>
+                    <classpath>
+                        <path path="@{classpath}"/>
+                    </classpath>
+                    <compilerarg line="${endorsed.classpath.cmd.line.arg}"/>
+                    <compilerarg line="${javac.profile.cmd.line.arg}"/>
+                    <compilerarg line="${javac.compilerargs}"/>
+                    <compilerarg value="-processorpath"/>
+                    <compilerarg path="@{processorpath}:${empty.dir}"/>
+                    <compilerarg line="${ap.processors.internal}"/>
+                    <compilerarg line="${annotation.processing.processor.options}"/>
+                    <compilerarg value="-s"/>
+                    <compilerarg path="@{apgeneratedsrcdir}"/>
+                    <compilerarg line="${ap.proc.none.internal}"/>
+                    <customize/>
+                </javac>
+            </sequential>
+        </macrodef>
+    </target>
+    <target depends="-init-ap-cmdline-properties" name="-init-macrodef-javac-without-processors" unless="ap.supported.internal">
+        <macrodef name="javac" uri="http://www.netbeans.org/ns/j2se-project/3">
+            <attribute default="${src.src.dir}" name="srcdir"/>
+            <attribute default="${build.classes.dir}" name="destdir"/>
+            <attribute default="${javac.classpath}" name="classpath"/>
+            <attribute default="${javac.processorpath}" name="processorpath"/>
+            <attribute default="${build.generated.sources.dir}/ap-source-output" name="apgeneratedsrcdir"/>
+            <attribute default="${includes}" name="includes"/>
+            <attribute default="${excludes}" name="excludes"/>
+            <attribute default="${javac.debug}" name="debug"/>
+            <attribute default="${empty.dir}" name="sourcepath"/>
+            <attribute default="${empty.dir}" name="gensrcdir"/>
+            <element name="customize" optional="true"/>
+            <sequential>
+                <property location="${build.dir}/empty" name="empty.dir"/>
+                <mkdir dir="${empty.dir}"/>
+                <javac debug="@{debug}" deprecation="${javac.deprecation}" destdir="@{destdir}" encoding="${source.encoding}" excludes="@{excludes}" fork="${javac.fork}" includeantruntime="false" includes="@{includes}" source="${javac.source}" sourcepath="@{sourcepath}" srcdir="@{srcdir}" target="${javac.target}" tempdir="${java.io.tmpdir}">
+                    <src>
+                        <dirset dir="@{gensrcdir}" erroronmissingdir="false">
+                            <include name="*"/>
+                        </dirset>
+                    </src>
+                    <classpath>
+                        <path path="@{classpath}"/>
+                    </classpath>
+                    <compilerarg line="${endorsed.classpath.cmd.line.arg}"/>
+                    <compilerarg line="${javac.profile.cmd.line.arg}"/>
+                    <compilerarg line="${javac.compilerargs}"/>
+                    <customize/>
+                </javac>
+            </sequential>
+        </macrodef>
+    </target>
+    <target depends="-init-macrodef-javac-with-processors,-init-macrodef-javac-without-processors" name="-init-macrodef-javac">
+        <macrodef name="depend" uri="http://www.netbeans.org/ns/j2se-project/3">
+            <attribute default="${src.src.dir}" name="srcdir"/>
+            <attribute default="${build.classes.dir}" name="destdir"/>
+            <attribute default="${javac.classpath}" name="classpath"/>
+            <sequential>
+                <depend cache="${build.dir}/depcache" destdir="@{destdir}" excludes="${excludes}" includes="${includes}" srcdir="@{srcdir}">
+                    <classpath>
+                        <path path="@{classpath}"/>
+                    </classpath>
+                </depend>
+            </sequential>
+        </macrodef>
+        <macrodef name="force-recompile" uri="http://www.netbeans.org/ns/j2se-project/3">
+            <attribute default="${build.classes.dir}" name="destdir"/>
+            <sequential>
+                <fail unless="javac.includes">Must set javac.includes</fail>
+                <pathconvert pathsep="${line.separator}" property="javac.includes.binary">
+                    <path>
+                        <filelist dir="@{destdir}" files="${javac.includes}"/>
+                    </path>
+                    <globmapper from="*.java" to="*.class"/>
+                </pathconvert>
+                <tempfile deleteonexit="true" property="javac.includesfile.binary"/>
+                <echo file="${javac.includesfile.binary}" message="${javac.includes.binary}"/>
+                <delete>
+                    <files includesfile="${javac.includesfile.binary}"/>
+                </delete>
+                <delete>
+                    <fileset file="${javac.includesfile.binary}"/>
+                </delete>
+            </sequential>
+        </macrodef>
+    </target>
+    <target if="${junit.available}" name="-init-macrodef-junit-init">
+        <condition else="false" property="nb.junit.batch" value="true">
+            <and>
+                <istrue value="${junit.available}"/>
+                <not>
+                    <isset property="test.method"/>
+                </not>
+            </and>
+        </condition>
+        <condition else="false" property="nb.junit.single" value="true">
+            <and>
+                <istrue value="${junit.available}"/>
+                <isset property="test.method"/>
+            </and>
+        </condition>
+    </target>
+    <target name="-init-test-properties">
+        <property name="test.binaryincludes" value="&lt;nothing&gt;"/>
+        <property name="test.binarytestincludes" value=""/>
+        <property name="test.binaryexcludes" value=""/>
+    </target>
+    <target if="${nb.junit.single}" name="-init-macrodef-junit-single" unless="${nb.junit.batch}">
+        <macrodef name="junit" uri="http://www.netbeans.org/ns/j2se-project/3">
+            <attribute default="${includes}" name="includes"/>
+            <attribute default="${excludes}" name="excludes"/>
+            <attribute default="**" name="testincludes"/>
+            <attribute default="" name="testmethods"/>
+            <element name="customize" optional="true"/>
+            <sequential>
+                <property name="junit.forkmode" value="perTest"/>
+                <junit dir="${work.dir}" errorproperty="tests.failed" failureproperty="tests.failed" fork="true" forkmode="${junit.forkmode}" showoutput="true" tempdir="${build.dir}">
+                    <test methods="@{testmethods}" name="@{testincludes}" todir="${build.test.results.dir}"/>
+                    <syspropertyset>
+                        <propertyref prefix="test-sys-prop."/>
+                        <mapper from="test-sys-prop.*" to="*" type="glob"/>
+                    </syspropertyset>
+                    <formatter type="brief" usefile="false"/>
+                    <formatter type="xml"/>
+                    <jvmarg value="-ea"/>
+                    <customize/>
+                </junit>
+            </sequential>
+        </macrodef>
+    </target>
+    <target depends="-init-test-properties" if="${nb.junit.batch}" name="-init-macrodef-junit-batch" unless="${nb.junit.single}">
+        <macrodef name="junit" uri="http://www.netbeans.org/ns/j2se-project/3">
+            <attribute default="${includes}" name="includes"/>
+            <attribute default="${excludes}" name="excludes"/>
+            <attribute default="**" name="testincludes"/>
+            <attribute default="" name="testmethods"/>
+            <element name="customize" optional="true"/>
+            <sequential>
+                <property name="junit.forkmode" value="perTest"/>
+                <junit dir="${work.dir}" errorproperty="tests.failed" failureproperty="tests.failed" fork="true" forkmode="${junit.forkmode}" showoutput="true" tempdir="${build.dir}">
+                    <batchtest todir="${build.test.results.dir}">
+                        <fileset dir="${build.test.classes.dir}" excludes="@{excludes},${excludes},${test.binaryexcludes}" includes="${test.binaryincludes}">
+                            <filename name="${test.binarytestincludes}"/>
+                        </fileset>
+                    </batchtest>
+                    <syspropertyset>
+                        <propertyref prefix="test-sys-prop."/>
+                        <mapper from="test-sys-prop.*" to="*" type="glob"/>
+                    </syspropertyset>
+                    <formatter type="brief" usefile="false"/>
+                    <formatter type="xml"/>
+                    <jvmarg value="-ea"/>
+                    <customize/>
+                </junit>
+            </sequential>
+        </macrodef>
+    </target>
+    <target depends="-init-macrodef-junit-init,-init-macrodef-junit-single, -init-macrodef-junit-batch" if="${junit.available}" name="-init-macrodef-junit"/>
+    <target if="${testng.available}" name="-init-macrodef-testng">
+        <macrodef name="testng" uri="http://www.netbeans.org/ns/j2se-project/3">
+            <attribute default="${includes}" name="includes"/>
+            <attribute default="${excludes}" name="excludes"/>
+            <attribute default="**" name="testincludes"/>
+            <attribute default="" name="testmethods"/>
+            <element name="customize" optional="true"/>
+            <sequential>
+                <condition else="" property="testng.methods.arg" value="@{testincludes}.@{testmethods}">
+                    <isset property="test.method"/>
+                </condition>
+                <union id="test.set"/>
+                <taskdef classname="org.testng.TestNGAntTask" classpath="${run.test.classpath}" name="testng"/>
+                <testng classfilesetref="test.set" failureProperty="tests.failed" listeners="org.testng.reporters.VerboseReporter" methods="${testng.methods.arg}" mode="${testng.mode}" outputdir="${build.test.results.dir}" suitename="JNLPConverter" testname="TestNG tests" workingDir="${work.dir}">
+                    <xmlfileset dir="${build.test.classes.dir}" includes="@{testincludes}"/>
+                    <propertyset>
+                        <propertyref prefix="test-sys-prop."/>
+                        <mapper from="test-sys-prop.*" to="*" type="glob"/>
+                    </propertyset>
+                    <customize/>
+                </testng>
+            </sequential>
+        </macrodef>
+    </target>
+    <target name="-init-macrodef-test-impl">
+        <macrodef name="test-impl" uri="http://www.netbeans.org/ns/j2se-project/3">
+            <attribute default="${includes}" name="includes"/>
+            <attribute default="${excludes}" name="excludes"/>
+            <attribute default="**" name="testincludes"/>
+            <attribute default="" name="testmethods"/>
+            <element implicit="true" name="customize" optional="true"/>
+            <sequential>
+                <echo>No tests executed.</echo>
+            </sequential>
+        </macrodef>
+    </target>
+    <target depends="-init-macrodef-junit" if="${junit.available}" name="-init-macrodef-junit-impl">
+        <macrodef name="test-impl" uri="http://www.netbeans.org/ns/j2se-project/3">
+            <attribute default="${includes}" name="includes"/>
+            <attribute default="${excludes}" name="excludes"/>
+            <attribute default="**" name="testincludes"/>
+            <attribute default="" name="testmethods"/>
+            <element implicit="true" name="customize" optional="true"/>
+            <sequential>
+                <j2seproject3:junit excludes="@{excludes}" includes="@{includes}" testincludes="@{testincludes}" testmethods="@{testmethods}">
+                    <customize/>
+                </j2seproject3:junit>
+            </sequential>
+        </macrodef>
+    </target>
+    <target depends="-init-macrodef-testng" if="${testng.available}" name="-init-macrodef-testng-impl">
+        <macrodef name="test-impl" uri="http://www.netbeans.org/ns/j2se-project/3">
+            <attribute default="${includes}" name="includes"/>
+            <attribute default="${excludes}" name="excludes"/>
+            <attribute default="**" name="testincludes"/>
+            <attribute default="" name="testmethods"/>
+            <element implicit="true" name="customize" optional="true"/>
+            <sequential>
+                <j2seproject3:testng excludes="@{excludes}" includes="@{includes}" testincludes="@{testincludes}" testmethods="@{testmethods}">
+                    <customize/>
+                </j2seproject3:testng>
+            </sequential>
+        </macrodef>
+    </target>
+    <target depends="-init-macrodef-test-impl,-init-macrodef-junit-impl,-init-macrodef-testng-impl" name="-init-macrodef-test">
+        <macrodef name="test" uri="http://www.netbeans.org/ns/j2se-project/3">
+            <attribute default="${includes}" name="includes"/>
+            <attribute default="${excludes}" name="excludes"/>
+            <attribute default="**" name="testincludes"/>
+            <attribute default="" name="testmethods"/>
+            <sequential>
+                <j2seproject3:test-impl excludes="@{excludes}" includes="@{includes}" testincludes="@{testincludes}" testmethods="@{testmethods}">
+                    <customize>
+                        <classpath>
+                            <path path="${run.test.classpath}"/>
+                        </classpath>
+                        <jvmarg line="${endorsed.classpath.cmd.line.arg}"/>
+                        <jvmarg line="${run.jvmargs}"/>
+                        <jvmarg line="${run.jvmargs.ide}"/>
+                    </customize>
+                </j2seproject3:test-impl>
+            </sequential>
+        </macrodef>
+    </target>
+    <target if="${junit.available}" name="-init-macrodef-junit-debug" unless="${nb.junit.batch}">
+        <macrodef name="junit-debug" uri="http://www.netbeans.org/ns/j2se-project/3">
+            <attribute default="${includes}" name="includes"/>
+            <attribute default="${excludes}" name="excludes"/>
+            <attribute default="**" name="testincludes"/>
+            <attribute default="" name="testmethods"/>
+            <element name="customize" optional="true"/>
+            <sequential>
+                <property name="junit.forkmode" value="perTest"/>
+                <junit dir="${work.dir}" errorproperty="tests.failed" failureproperty="tests.failed" fork="true" forkmode="${junit.forkmode}" showoutput="true" tempdir="${build.dir}">
+                    <test methods="@{testmethods}" name="@{testincludes}" todir="${build.test.results.dir}"/>
+                    <syspropertyset>
+                        <propertyref prefix="test-sys-prop."/>
+                        <mapper from="test-sys-prop.*" to="*" type="glob"/>
+                    </syspropertyset>
+                    <formatter type="brief" usefile="false"/>
+                    <formatter type="xml"/>
+                    <jvmarg value="-ea"/>
+                    <jvmarg line="${debug-args-line}"/>
+                    <jvmarg value="-Xrunjdwp:transport=${debug-transport},address=${jpda.address}"/>
+                    <customize/>
+                </junit>
+            </sequential>
+        </macrodef>
+    </target>
+    <target depends="-init-test-properties" if="${nb.junit.batch}" name="-init-macrodef-junit-debug-batch">
+        <macrodef name="junit-debug" uri="http://www.netbeans.org/ns/j2se-project/3">
+            <attribute default="${includes}" name="includes"/>
+            <attribute default="${excludes}" name="excludes"/>
+            <attribute default="**" name="testincludes"/>
+            <attribute default="" name="testmethods"/>
+            <element name="customize" optional="true"/>
+            <sequential>
+                <property name="junit.forkmode" value="perTest"/>
+                <junit dir="${work.dir}" errorproperty="tests.failed" failureproperty="tests.failed" fork="true" forkmode="${junit.forkmode}" showoutput="true" tempdir="${build.dir}">
+                    <batchtest todir="${build.test.results.dir}">
+                        <fileset dir="${build.test.classes.dir}" excludes="@{excludes},${excludes},${test.binaryexcludes}" includes="${test.binaryincludes}">
+                            <filename name="${test.binarytestincludes}"/>
+                        </fileset>
+                    </batchtest>
+                    <syspropertyset>
+                        <propertyref prefix="test-sys-prop."/>
+                        <mapper from="test-sys-prop.*" to="*" type="glob"/>
+                    </syspropertyset>
+                    <formatter type="brief" usefile="false"/>
+                    <formatter type="xml"/>
+                    <jvmarg value="-ea"/>
+                    <jvmarg line="${debug-args-line}"/>
+                    <jvmarg value="-Xrunjdwp:transport=${debug-transport},address=${jpda.address}"/>
+                    <customize/>
+                </junit>
+            </sequential>
+        </macrodef>
+    </target>
+    <target depends="-init-macrodef-junit-debug,-init-macrodef-junit-debug-batch" if="${junit.available}" name="-init-macrodef-junit-debug-impl">
+        <macrodef name="test-debug-impl" uri="http://www.netbeans.org/ns/j2se-project/3">
+            <attribute default="${includes}" name="includes"/>
+            <attribute default="${excludes}" name="excludes"/>
+            <attribute default="**" name="testincludes"/>
+            <attribute default="" name="testmethods"/>
+            <element implicit="true" name="customize" optional="true"/>
+            <sequential>
+                <j2seproject3:junit-debug excludes="@{excludes}" includes="@{includes}" testincludes="@{testincludes}" testmethods="@{testmethods}">
+                    <customize/>
+                </j2seproject3:junit-debug>
+            </sequential>
+        </macrodef>
+    </target>
+    <target if="${testng.available}" name="-init-macrodef-testng-debug">
+        <macrodef name="testng-debug" uri="http://www.netbeans.org/ns/j2se-project/3">
+            <attribute default="${main.class}" name="testClass"/>
+            <attribute default="" name="testMethod"/>
+            <element name="customize2" optional="true"/>
+            <sequential>
+                <condition else="-testclass @{testClass}" property="test.class.or.method" value="-methods @{testClass}.@{testMethod}">
+                    <isset property="test.method"/>
+                </condition>
+                <condition else="-suitename JNLPConverter -testname @{testClass} ${test.class.or.method}" property="testng.cmd.args" value="@{testClass}">
+                    <matches pattern=".*\.xml" string="@{testClass}"/>
+                </condition>
+                <delete dir="${build.test.results.dir}" quiet="true"/>
+                <mkdir dir="${build.test.results.dir}"/>
+                <j2seproject3:debug classname="org.testng.TestNG" classpath="${debug.test.classpath}">
+                    <customize>
+                        <customize2/>
+                        <jvmarg value="-ea"/>
+                        <arg line="${testng.debug.mode}"/>
+                        <arg line="-d ${build.test.results.dir}"/>
+                        <arg line="-listener org.testng.reporters.VerboseReporter"/>
+                        <arg line="${testng.cmd.args}"/>
+                    </customize>
+                </j2seproject3:debug>
+            </sequential>
+        </macrodef>
+    </target>
+    <target depends="-init-macrodef-testng-debug" if="${testng.available}" name="-init-macrodef-testng-debug-impl">
+        <macrodef name="testng-debug-impl" uri="http://www.netbeans.org/ns/j2se-project/3">
+            <attribute default="${main.class}" name="testClass"/>
+            <attribute default="" name="testMethod"/>
+            <element implicit="true" name="customize2" optional="true"/>
+            <sequential>
+                <j2seproject3:testng-debug testClass="@{testClass}" testMethod="@{testMethod}">
+                    <customize2/>
+                </j2seproject3:testng-debug>
+            </sequential>
+        </macrodef>
+    </target>
+    <target depends="-init-macrodef-junit-debug-impl" if="${junit.available}" name="-init-macrodef-test-debug-junit">
+        <macrodef name="test-debug" uri="http://www.netbeans.org/ns/j2se-project/3">
+            <attribute default="${includes}" name="includes"/>
+            <attribute default="${excludes}" name="excludes"/>
+            <attribute default="**" name="testincludes"/>
+            <attribute default="" name="testmethods"/>
+            <attribute default="${main.class}" name="testClass"/>
+            <attribute default="" name="testMethod"/>
+            <sequential>
+                <j2seproject3:test-debug-impl excludes="@{excludes}" includes="@{includes}" testincludes="@{testincludes}" testmethods="@{testmethods}">
+                    <customize>
+                        <classpath>
+                            <path path="${run.test.classpath}"/>
+                        </classpath>
+                        <jvmarg line="${endorsed.classpath.cmd.line.arg}"/>
+                        <jvmarg line="${run.jvmargs}"/>
+                        <jvmarg line="${run.jvmargs.ide}"/>
+                    </customize>
+                </j2seproject3:test-debug-impl>
+            </sequential>
+        </macrodef>
+    </target>
+    <target depends="-init-macrodef-testng-debug-impl" if="${testng.available}" name="-init-macrodef-test-debug-testng">
+        <macrodef name="test-debug" uri="http://www.netbeans.org/ns/j2se-project/3">
+            <attribute default="${includes}" name="includes"/>
+            <attribute default="${excludes}" name="excludes"/>
+            <attribute default="**" name="testincludes"/>
+            <attribute default="" name="testmethods"/>
+            <attribute default="${main.class}" name="testClass"/>
+            <attribute default="" name="testMethod"/>
+            <sequential>
+                <j2seproject3:testng-debug-impl testClass="@{testClass}" testMethod="@{testMethod}">
+                    <customize2>
+                        <syspropertyset>
+                            <propertyref prefix="test-sys-prop."/>
+                            <mapper from="test-sys-prop.*" to="*" type="glob"/>
+                        </syspropertyset>
+                    </customize2>
+                </j2seproject3:testng-debug-impl>
+            </sequential>
+        </macrodef>
+    </target>
+    <target depends="-init-macrodef-test-debug-junit,-init-macrodef-test-debug-testng" name="-init-macrodef-test-debug"/>
+    <!--
+                pre NB7.2 profiling section; consider it deprecated
+            -->
+    <target depends="-profile-pre-init, init, -profile-post-init, -profile-init-macrodef-profile, -profile-init-check" if="profiler.info.jvmargs.agent" name="profile-init"/>
+    <target if="profiler.info.jvmargs.agent" name="-profile-pre-init">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target if="profiler.info.jvmargs.agent" name="-profile-post-init">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target if="profiler.info.jvmargs.agent" name="-profile-init-macrodef-profile">
+        <macrodef name="resolve">
+            <attribute name="name"/>
+            <attribute name="value"/>
+            <sequential>
+                <property name="@{name}" value="${env.@{value}}"/>
+            </sequential>
+        </macrodef>
+        <macrodef name="profile">
+            <attribute default="${main.class}" name="classname"/>
+            <element name="customize" optional="true"/>
+            <sequential>
+                <property environment="env"/>
+                <resolve name="profiler.current.path" value="${profiler.info.pathvar}"/>
+                <java classname="@{classname}" dir="${profiler.info.dir}" failonerror="${java.failonerror}" fork="true" jvm="${profiler.info.jvm}">
+                    <jvmarg line="${endorsed.classpath.cmd.line.arg}"/>
+                    <jvmarg value="${profiler.info.jvmargs.agent}"/>
+                    <jvmarg line="${profiler.info.jvmargs}"/>
+                    <env key="${profiler.info.pathvar}" path="${profiler.info.agentpath}:${profiler.current.path}"/>
+                    <arg line="${application.args}"/>
+                    <classpath>
+                        <path path="${run.classpath}"/>
+                    </classpath>
+                    <syspropertyset>
+                        <propertyref prefix="run-sys-prop."/>
+                        <mapper from="run-sys-prop.*" to="*" type="glob"/>
+                    </syspropertyset>
+                    <customize/>
+                </java>
+            </sequential>
+        </macrodef>
+    </target>
+    <target depends="-profile-pre-init, init, -profile-post-init, -profile-init-macrodef-profile" if="profiler.info.jvmargs.agent" name="-profile-init-check">
+        <fail unless="profiler.info.jvm">Must set JVM to use for profiling in profiler.info.jvm</fail>
+        <fail unless="profiler.info.jvmargs.agent">Must set profiler agent JVM arguments in profiler.info.jvmargs.agent</fail>
+    </target>
+    <!--
+                end of pre NB7.2 profiling section
+            -->
+    <target depends="-init-debug-args" name="-init-macrodef-nbjpda">
+        <macrodef name="nbjpdastart" uri="http://www.netbeans.org/ns/j2se-project/1">
+            <attribute default="${main.class}" name="name"/>
+            <attribute default="${debug.classpath}" name="classpath"/>
+            <attribute default="" name="stopclassname"/>
+            <sequential>
+                <nbjpdastart addressproperty="jpda.address" name="@{name}" stopclassname="@{stopclassname}" transport="${debug-transport}">
+                    <classpath>
+                        <path path="@{classpath}"/>
+                    </classpath>
+                </nbjpdastart>
+            </sequential>
+        </macrodef>
+        <macrodef name="nbjpdareload" uri="http://www.netbeans.org/ns/j2se-project/1">
+            <attribute default="${build.classes.dir}" name="dir"/>
+            <sequential>
+                <nbjpdareload>
+                    <fileset dir="@{dir}" includes="${fix.classes}">
+                        <include name="${fix.includes}*.class"/>
+                    </fileset>
+                </nbjpdareload>
+            </sequential>
+        </macrodef>
+    </target>
+    <target name="-init-debug-args">
+        <property name="version-output" value="java version &quot;${ant.java.version}"/>
+        <condition property="have-jdk-older-than-1.4">
+            <or>
+                <contains string="${version-output}" substring="java version &quot;1.0"/>
+                <contains string="${version-output}" substring="java version &quot;1.1"/>
+                <contains string="${version-output}" substring="java version &quot;1.2"/>
+                <contains string="${version-output}" substring="java version &quot;1.3"/>
+            </or>
+        </condition>
+        <condition else="-Xdebug" property="debug-args-line" value="-Xdebug -Xnoagent -Djava.compiler=none">
+            <istrue value="${have-jdk-older-than-1.4}"/>
+        </condition>
+        <condition else="dt_socket" property="debug-transport-by-os" value="dt_shmem">
+            <os family="windows"/>
+        </condition>
+        <condition else="${debug-transport-by-os}" property="debug-transport" value="${debug.transport}">
+            <isset property="debug.transport"/>
+        </condition>
+    </target>
+    <target depends="-init-debug-args" name="-init-macrodef-debug">
+        <macrodef name="debug" uri="http://www.netbeans.org/ns/j2se-project/3">
+            <attribute default="${main.class}" name="classname"/>
+            <attribute default="${debug.classpath}" name="classpath"/>
+            <element name="customize" optional="true"/>
+            <sequential>
+                <java classname="@{classname}" dir="${work.dir}" failonerror="${java.failonerror}" fork="true">
+                    <jvmarg line="${endorsed.classpath.cmd.line.arg}"/>
+                    <jvmarg line="${debug-args-line}"/>
+                    <jvmarg value="-Xrunjdwp:transport=${debug-transport},address=${jpda.address}"/>
+                    <jvmarg value="-Dfile.encoding=${runtime.encoding}"/>
+                    <redirector errorencoding="${runtime.encoding}" inputencoding="${runtime.encoding}" outputencoding="${runtime.encoding}"/>
+                    <jvmarg line="${run.jvmargs}"/>
+                    <jvmarg line="${run.jvmargs.ide}"/>
+                    <classpath>
+                        <path path="@{classpath}"/>
+                    </classpath>
+                    <syspropertyset>
+                        <propertyref prefix="run-sys-prop."/>
+                        <mapper from="run-sys-prop.*" to="*" type="glob"/>
+                    </syspropertyset>
+                    <customize/>
+                </java>
+            </sequential>
+        </macrodef>
+    </target>
+    <target name="-init-macrodef-java">
+        <macrodef name="java" uri="http://www.netbeans.org/ns/j2se-project/1">
+            <attribute default="${main.class}" name="classname"/>
+            <attribute default="${run.classpath}" name="classpath"/>
+            <attribute default="jvm" name="jvm"/>
+            <element name="customize" optional="true"/>
+            <sequential>
+                <java classname="@{classname}" dir="${work.dir}" failonerror="${java.failonerror}" fork="true">
+                    <jvmarg line="${endorsed.classpath.cmd.line.arg}"/>
+                    <jvmarg value="-Dfile.encoding=${runtime.encoding}"/>
+                    <redirector errorencoding="${runtime.encoding}" inputencoding="${runtime.encoding}" outputencoding="${runtime.encoding}"/>
+                    <jvmarg line="${run.jvmargs}"/>
+                    <jvmarg line="${run.jvmargs.ide}"/>
+                    <classpath>
+                        <path path="@{classpath}"/>
+                    </classpath>
+                    <syspropertyset>
+                        <propertyref prefix="run-sys-prop."/>
+                        <mapper from="run-sys-prop.*" to="*" type="glob"/>
+                    </syspropertyset>
+                    <customize/>
+                </java>
+            </sequential>
+        </macrodef>
+    </target>
+    <target name="-init-macrodef-copylibs">
+        <macrodef name="copylibs" uri="http://www.netbeans.org/ns/j2se-project/3">
+            <attribute default="${manifest.file}" name="manifest"/>
+            <element name="customize" optional="true"/>
+            <sequential>
+                <property location="${build.classes.dir}" name="build.classes.dir.resolved"/>
+                <pathconvert property="run.classpath.without.build.classes.dir">
+                    <path path="${run.classpath}"/>
+                    <map from="${build.classes.dir.resolved}" to=""/>
+                </pathconvert>
+                <pathconvert pathsep=" " property="jar.classpath">
+                    <path path="${run.classpath.without.build.classes.dir}"/>
+                    <chainedmapper>
+                        <flattenmapper/>
+                        <filtermapper>
+                            <replacestring from=" " to="%20"/>
+                        </filtermapper>
+                        <globmapper from="*" to="lib/*"/>
+                    </chainedmapper>
+                </pathconvert>
+                <taskdef classname="org.netbeans.modules.java.j2seproject.copylibstask.CopyLibs" classpath="${libs.CopyLibs.classpath}" name="copylibs"/>
+                <copylibs compress="${jar.compress}" excludeFromCopy="${copylibs.excludes}" index="${jar.index}" indexMetaInf="${jar.index.metainf}" jarfile="${dist.jar}" manifest="@{manifest}" manifestencoding="UTF-8" rebase="${copylibs.rebase}" runtimeclasspath="${run.classpath.without.build.classes.dir}">
+                    <fileset dir="${build.classes.dir}" excludes="${dist.archive.excludes}"/>
+                    <manifest>
+                        <attribute name="Class-Path" value="${jar.classpath}"/>
+                        <customize/>
+                    </manifest>
+                </copylibs>
+            </sequential>
+        </macrodef>
+    </target>
+    <target name="-init-presetdef-jar">
+        <presetdef name="jar" uri="http://www.netbeans.org/ns/j2se-project/1">
+            <jar compress="${jar.compress}" index="${jar.index}" jarfile="${dist.jar}" manifestencoding="UTF-8">
+                <j2seproject1:fileset dir="${build.classes.dir}" excludes="${dist.archive.excludes}"/>
+            </jar>
+        </presetdef>
+    </target>
+    <target name="-init-ap-cmdline-properties">
+        <property name="annotation.processing.enabled" value="true"/>
+        <property name="annotation.processing.processors.list" value=""/>
+        <property name="annotation.processing.processor.options" value=""/>
+        <property name="annotation.processing.run.all.processors" value="true"/>
+        <property name="javac.processorpath" value="${javac.classpath}"/>
+        <property name="javac.test.processorpath" value="${javac.test.classpath}"/>
+        <condition property="ap.supported.internal" value="true">
+            <not>
+                <matches pattern="1\.[0-5](\..*)?" string="${javac.source}"/>
+            </not>
+        </condition>
+    </target>
+    <target depends="-init-ap-cmdline-properties" if="ap.supported.internal" name="-init-ap-cmdline-supported">
+        <condition else="" property="ap.processors.internal" value="-processor ${annotation.processing.processors.list}">
+            <isfalse value="${annotation.processing.run.all.processors}"/>
+        </condition>
+        <condition else="" property="ap.proc.none.internal" value="-proc:none">
+            <isfalse value="${annotation.processing.enabled}"/>
+        </condition>
+    </target>
+    <target depends="-init-ap-cmdline-properties,-init-ap-cmdline-supported" name="-init-ap-cmdline">
+        <property name="ap.cmd.line.internal" value=""/>
+    </target>
+    <target depends="-pre-init,-init-private,-init-user,-init-project,-do-init,-post-init,-init-check,-init-macrodef-property,-init-macrodef-javac,-init-macrodef-test,-init-macrodef-test-debug,-init-macrodef-nbjpda,-init-macrodef-debug,-init-macrodef-java,-init-presetdef-jar,-init-ap-cmdline" name="init"/>
+    <!--
+                ===================
+                COMPILATION SECTION
+                ===================
+            -->
+    <target name="-deps-jar-init" unless="built-jar.properties">
+        <property location="${build.dir}/built-jar.properties" name="built-jar.properties"/>
+        <delete file="${built-jar.properties}" quiet="true"/>
+    </target>
+    <target if="already.built.jar.${basedir}" name="-warn-already-built-jar">
+        <echo level="warn" message="Cycle detected: JNLPConverter was already built"/>
+    </target>
+    <target depends="init,-deps-jar-init" name="deps-jar" unless="no.deps">
+        <mkdir dir="${build.dir}"/>
+        <touch file="${built-jar.properties}" verbose="false"/>
+        <property file="${built-jar.properties}" prefix="already.built.jar."/>
+        <antcall target="-warn-already-built-jar"/>
+        <propertyfile file="${built-jar.properties}">
+            <entry key="${basedir}" value=""/>
+        </propertyfile>
+    </target>
+    <target depends="init,-check-automatic-build,-clean-after-automatic-build" name="-verify-automatic-build"/>
+    <target depends="init" name="-check-automatic-build">
+        <available file="${build.classes.dir}/.netbeans_automatic_build" property="netbeans.automatic.build"/>
+    </target>
+    <target depends="init" if="netbeans.automatic.build" name="-clean-after-automatic-build">
+        <antcall target="clean"/>
+    </target>
+    <target depends="init,deps-jar" name="-pre-pre-compile">
+        <mkdir dir="${build.classes.dir}"/>
+    </target>
+    <target name="-pre-compile">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target if="do.depend.true" name="-compile-depend">
+        <pathconvert property="build.generated.subdirs">
+            <dirset dir="${build.generated.sources.dir}" erroronmissingdir="false">
+                <include name="*"/>
+            </dirset>
+        </pathconvert>
+        <j2seproject3:depend srcdir="${src.src.dir}:${build.generated.subdirs}"/>
+    </target>
+    <target depends="init,deps-jar,-pre-pre-compile,-pre-compile, -copy-persistence-xml,-compile-depend" if="have.sources" name="-do-compile">
+        <j2seproject3:javac gensrcdir="${build.generated.sources.dir}"/>
+        <copy todir="${build.classes.dir}">
+            <fileset dir="${src.src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
+        </copy>
+    </target>
+    <target if="has.persistence.xml" name="-copy-persistence-xml">
+        <mkdir dir="${build.classes.dir}/META-INF"/>
+        <copy todir="${build.classes.dir}/META-INF">
+            <fileset dir="${meta.inf.dir}" includes="persistence.xml orm.xml"/>
+        </copy>
+    </target>
+    <target name="-post-compile">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target depends="init,deps-jar,-verify-automatic-build,-pre-pre-compile,-pre-compile,-do-compile,-post-compile" description="Compile project." name="compile"/>
+    <target name="-pre-compile-single">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target depends="init,deps-jar,-pre-pre-compile" name="-do-compile-single">
+        <fail unless="javac.includes">Must select some files in the IDE or set javac.includes</fail>
+        <j2seproject3:force-recompile/>
+        <j2seproject3:javac excludes="" gensrcdir="${build.generated.sources.dir}" includes="${javac.includes}" sourcepath="${src.src.dir}"/>
+    </target>
+    <target name="-post-compile-single">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target depends="init,deps-jar,-verify-automatic-build,-pre-pre-compile,-pre-compile-single,-do-compile-single,-post-compile-single" name="compile-single"/>
+    <!--
+                ====================
+                JAR BUILDING SECTION
+                ====================
+            -->
+    <target depends="init" name="-pre-pre-jar">
+        <dirname file="${dist.jar}" property="dist.jar.dir"/>
+        <mkdir dir="${dist.jar.dir}"/>
+    </target>
+    <target name="-pre-jar">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target depends="init" if="do.archive" name="-do-jar-create-manifest" unless="manifest.available">
+        <tempfile deleteonexit="true" destdir="${build.dir}" property="tmp.manifest.file"/>
+        <touch file="${tmp.manifest.file}" verbose="false"/>
+    </target>
+    <target depends="init" if="do.archive+manifest.available" name="-do-jar-copy-manifest">
+        <tempfile deleteonexit="true" destdir="${build.dir}" property="tmp.manifest.file"/>
+        <copy encoding="${manifest.encoding}" file="${manifest.file}" outputencoding="UTF-8" tofile="${tmp.manifest.file}"/>
+    </target>
+    <target depends="init,-do-jar-create-manifest,-do-jar-copy-manifest" if="do.archive+main.class.available" name="-do-jar-set-mainclass">
+        <manifest encoding="UTF-8" file="${tmp.manifest.file}" mode="update">
+            <attribute name="Main-Class" value="${main.class}"/>
+        </manifest>
+    </target>
+    <target depends="init,-do-jar-create-manifest,-do-jar-copy-manifest" if="do.archive+profile.available" name="-do-jar-set-profile">
+        <manifest encoding="UTF-8" file="${tmp.manifest.file}" mode="update">
+            <attribute name="Profile" value="${javac.profile}"/>
+        </manifest>
+    </target>
+    <target depends="init,-do-jar-create-manifest,-do-jar-copy-manifest" if="do.archive+splashscreen.available" name="-do-jar-set-splashscreen">
+        <basename file="${application.splash}" property="splashscreen.basename"/>
+        <mkdir dir="${build.classes.dir}/META-INF"/>
+        <copy failonerror="false" file="${application.splash}" todir="${build.classes.dir}/META-INF"/>
+        <manifest encoding="UTF-8" file="${tmp.manifest.file}" mode="update">
+            <attribute name="SplashScreen-Image" value="META-INF/${splashscreen.basename}"/>
+        </manifest>
+    </target>
+    <target depends="init,-init-macrodef-copylibs,compile,-pre-pre-jar,-pre-jar,-do-jar-create-manifest,-do-jar-copy-manifest,-do-jar-set-mainclass,-do-jar-set-profile,-do-jar-set-splashscreen" if="do.mkdist" name="-do-jar-copylibs">
+        <j2seproject3:copylibs manifest="${tmp.manifest.file}"/>
+        <echo level="info">To run this application from the command line without Ant, try:</echo>
+        <property location="${dist.jar}" name="dist.jar.resolved"/>
+        <echo level="info">java -jar "${dist.jar.resolved}"</echo>
+    </target>
+    <target depends="init,compile,-pre-pre-jar,-pre-jar,-do-jar-create-manifest,-do-jar-copy-manifest,-do-jar-set-mainclass,-do-jar-set-profile,-do-jar-set-splashscreen" if="do.archive" name="-do-jar-jar" unless="do.mkdist">
+        <j2seproject1:jar manifest="${tmp.manifest.file}"/>
+        <property location="${build.classes.dir}" name="build.classes.dir.resolved"/>
+        <property location="${dist.jar}" name="dist.jar.resolved"/>
+        <pathconvert property="run.classpath.with.dist.jar">
+            <path path="${run.classpath}"/>
+            <map from="${build.classes.dir.resolved}" to="${dist.jar.resolved}"/>
+        </pathconvert>
+        <condition else="" property="jar.usage.message" value="To run this application from the command line without Ant, try:${line.separator}${platform.java} -cp ${run.classpath.with.dist.jar} ${main.class}">
+            <isset property="main.class.available"/>
+        </condition>
+        <condition else="debug" property="jar.usage.level" value="info">
+            <isset property="main.class.available"/>
+        </condition>
+        <echo level="${jar.usage.level}" message="${jar.usage.message}"/>
+    </target>
+    <target depends="-do-jar-copylibs" if="do.archive" name="-do-jar-delete-manifest">
+        <delete>
+            <fileset file="${tmp.manifest.file}"/>
+        </delete>
+    </target>
+    <target depends="init,compile,-pre-pre-jar,-pre-jar,-do-jar-create-manifest,-do-jar-copy-manifest,-do-jar-set-mainclass,-do-jar-set-profile,-do-jar-set-splashscreen,-do-jar-jar,-do-jar-delete-manifest" name="-do-jar-without-libraries"/>
+    <target depends="init,compile,-pre-pre-jar,-pre-jar,-do-jar-create-manifest,-do-jar-copy-manifest,-do-jar-set-mainclass,-do-jar-set-profile,-do-jar-set-splashscreen,-do-jar-copylibs,-do-jar-delete-manifest" name="-do-jar-with-libraries"/>
+    <target name="-post-jar">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target depends="init,compile,-pre-jar,-do-jar-without-libraries,-do-jar-with-libraries,-post-jar" name="-do-jar"/>
+    <target depends="init,compile,-pre-jar,-do-jar,-post-jar" description="Build JAR." name="jar"/>
+    <!--
+                =================
+                EXECUTION SECTION
+                =================
+            -->
+    <target depends="init,compile" description="Run a main class." name="run">
+        <j2seproject1:java>
+            <customize>
+                <arg line="${application.args}"/>
+            </customize>
+        </j2seproject1:java>
+    </target>
+    <target name="-do-not-recompile">
+        <property name="javac.includes.binary" value=""/>
+    </target>
+    <target depends="init,compile-single" name="run-single">
+        <fail unless="run.class">Must select one file in the IDE or set run.class</fail>
+        <j2seproject1:java classname="${run.class}"/>
+    </target>
+    <target depends="init,compile-test-single" name="run-test-with-main">
+        <fail unless="run.class">Must select one file in the IDE or set run.class</fail>
+        <j2seproject1:java classname="${run.class}" classpath="${run.test.classpath}"/>
+    </target>
+    <!--
+                =================
+                DEBUGGING SECTION
+                =================
+            -->
+    <target depends="init" if="netbeans.home" name="-debug-start-debugger">
+        <j2seproject1:nbjpdastart name="${debug.class}"/>
+    </target>
+    <target depends="init" if="netbeans.home" name="-debug-start-debugger-main-test">
+        <j2seproject1:nbjpdastart classpath="${debug.test.classpath}" name="${debug.class}"/>
+    </target>
+    <target depends="init,compile" name="-debug-start-debuggee">
+        <j2seproject3:debug>
+            <customize>
+                <arg line="${application.args}"/>
+            </customize>
+        </j2seproject3:debug>
+    </target>
+    <target depends="init,compile,-debug-start-debugger,-debug-start-debuggee" description="Debug project in IDE." if="netbeans.home" name="debug"/>
+    <target depends="init" if="netbeans.home" name="-debug-start-debugger-stepinto">
+        <j2seproject1:nbjpdastart stopclassname="${main.class}"/>
+    </target>
+    <target depends="init,compile,-debug-start-debugger-stepinto,-debug-start-debuggee" if="netbeans.home" name="debug-stepinto"/>
+    <target depends="init,compile-single" if="netbeans.home" name="-debug-start-debuggee-single">
+        <fail unless="debug.class">Must select one file in the IDE or set debug.class</fail>
+        <j2seproject3:debug classname="${debug.class}"/>
+    </target>
+    <target depends="init,compile-single,-debug-start-debugger,-debug-start-debuggee-single" if="netbeans.home" name="debug-single"/>
+    <target depends="init,compile-test-single" if="netbeans.home" name="-debug-start-debuggee-main-test">
+        <fail unless="debug.class">Must select one file in the IDE or set debug.class</fail>
+        <j2seproject3:debug classname="${debug.class}" classpath="${debug.test.classpath}"/>
+    </target>
+    <target depends="init,compile-test-single,-debug-start-debugger-main-test,-debug-start-debuggee-main-test" if="netbeans.home" name="debug-test-with-main"/>
+    <target depends="init" name="-pre-debug-fix">
+        <fail unless="fix.includes">Must set fix.includes</fail>
+        <property name="javac.includes" value="${fix.includes}.java"/>
+    </target>
+    <target depends="init,-pre-debug-fix,compile-single" if="netbeans.home" name="-do-debug-fix">
+        <j2seproject1:nbjpdareload/>
+    </target>
+    <target depends="init,-pre-debug-fix,-do-debug-fix" if="netbeans.home" name="debug-fix"/>
+    <!--
+                =================
+                PROFILING SECTION
+                =================
+            -->
+    <!--
+                pre NB7.2 profiler integration
+            -->
+    <target depends="profile-init,compile" description="Profile a project in the IDE." if="profiler.info.jvmargs.agent" name="-profile-pre72">
+        <fail unless="netbeans.home">This target only works when run from inside the NetBeans IDE.</fail>
+        <nbprofiledirect>
+            <classpath>
+                <path path="${run.classpath}"/>
+            </classpath>
+        </nbprofiledirect>
+        <profile/>
+    </target>
+    <target depends="profile-init,compile-single" description="Profile a selected class in the IDE." if="profiler.info.jvmargs.agent" name="-profile-single-pre72">
+        <fail unless="profile.class">Must select one file in the IDE or set profile.class</fail>
+        <fail unless="netbeans.home">This target only works when run from inside the NetBeans IDE.</fail>
+        <nbprofiledirect>
+            <classpath>
+                <path path="${run.classpath}"/>
+            </classpath>
+        </nbprofiledirect>
+        <profile classname="${profile.class}"/>
+    </target>
+    <target depends="profile-init,compile-single" if="profiler.info.jvmargs.agent" name="-profile-applet-pre72">
+        <fail unless="netbeans.home">This target only works when run from inside the NetBeans IDE.</fail>
+        <nbprofiledirect>
+            <classpath>
+                <path path="${run.classpath}"/>
+            </classpath>
+        </nbprofiledirect>
+        <profile classname="sun.applet.AppletViewer">
+            <customize>
+                <arg value="${applet.url}"/>
+            </customize>
+        </profile>
+    </target>
+    <target depends="profile-init,compile-test-single" if="profiler.info.jvmargs.agent" name="-profile-test-single-pre72">
+        <fail unless="netbeans.home">This target only works when run from inside the NetBeans IDE.</fail>
+        <nbprofiledirect>
+            <classpath>
+                <path path="${run.test.classpath}"/>
+            </classpath>
+        </nbprofiledirect>
+        <junit dir="${profiler.info.dir}" errorproperty="tests.failed" failureproperty="tests.failed" fork="true" jvm="${profiler.info.jvm}" showoutput="true">
+            <env key="${profiler.info.pathvar}" path="${profiler.info.agentpath}:${profiler.current.path}"/>
+            <jvmarg value="${profiler.info.jvmargs.agent}"/>
+            <jvmarg line="${profiler.info.jvmargs}"/>
+            <test name="${profile.class}"/>
+            <classpath>
+                <path path="${run.test.classpath}"/>
+            </classpath>
+            <syspropertyset>
+                <propertyref prefix="test-sys-prop."/>
+                <mapper from="test-sys-prop.*" to="*" type="glob"/>
+            </syspropertyset>
+            <formatter type="brief" usefile="false"/>
+            <formatter type="xml"/>
+        </junit>
+    </target>
+    <!--
+                end of pre NB72 profiling section
+            -->
+    <target if="netbeans.home" name="-profile-check">
+        <condition property="profiler.configured">
+            <or>
+                <contains casesensitive="true" string="${run.jvmargs.ide}" substring="-agentpath:"/>
+                <contains casesensitive="true" string="${run.jvmargs.ide}" substring="-javaagent:"/>
+            </or>
+        </condition>
+    </target>
+    <target depends="-profile-check,-profile-pre72" description="Profile a project in the IDE." if="profiler.configured" name="profile" unless="profiler.info.jvmargs.agent">
+        <startprofiler/>
+        <antcall target="run"/>
+    </target>
+    <target depends="-profile-check,-profile-single-pre72" description="Profile a selected class in the IDE." if="profiler.configured" name="profile-single" unless="profiler.info.jvmargs.agent">
+        <fail unless="run.class">Must select one file in the IDE or set run.class</fail>
+        <startprofiler/>
+        <antcall target="run-single"/>
+    </target>
+    <target depends="-profile-test-single-pre72" description="Profile a selected test in the IDE." name="profile-test-single"/>
+    <target depends="-profile-check" description="Profile a selected test in the IDE." if="profiler.configured" name="profile-test" unless="profiler.info.jvmargs">
+        <fail unless="test.includes">Must select some files in the IDE or set test.includes</fail>
+        <startprofiler/>
+        <antcall target="test-single"/>
+    </target>
+    <target depends="-profile-check" description="Profile a selected class in the IDE." if="profiler.configured" name="profile-test-with-main">
+        <fail unless="run.class">Must select one file in the IDE or set run.class</fail>
+        <startprofiler/>
+        <antcall target="run-test-with-main"/>
+    </target>
+    <target depends="-profile-check,-profile-applet-pre72" if="profiler.configured" name="profile-applet" unless="profiler.info.jvmargs.agent">
+        <fail unless="applet.url">Must select one file in the IDE or set applet.url</fail>
+        <startprofiler/>
+        <antcall target="run-applet"/>
+    </target>
+    <!--
+                ===============
+                JAVADOC SECTION
+                ===============
+            -->
+    <target depends="init" if="have.sources" name="-javadoc-build">
+        <mkdir dir="${dist.javadoc.dir}"/>
+        <condition else="" property="javadoc.endorsed.classpath.cmd.line.arg" value="-J${endorsed.classpath.cmd.line.arg}">
+            <and>
+                <isset property="endorsed.classpath.cmd.line.arg"/>
+                <not>
+                    <equals arg1="${endorsed.classpath.cmd.line.arg}" arg2=""/>
+                </not>
+            </and>
+        </condition>
+        <condition else="" property="bug5101868workaround" value="*.java">
+            <matches pattern="1\.[56](\..*)?" string="${java.version}"/>
+        </condition>
+        <javadoc additionalparam="-J-Dfile.encoding=${file.encoding} ${javadoc.additionalparam}" author="${javadoc.author}" charset="UTF-8" destdir="${dist.javadoc.dir}" docencoding="UTF-8" encoding="${javadoc.encoding.used}" failonerror="true" noindex="${javadoc.noindex}" nonavbar="${javadoc.nonavbar}" notree="${javadoc.notree}" private="${javadoc.private}" source="${javac.source}" splitindex="${javadoc.splitindex}" use="${javadoc.use}" useexternalfile="true" version="${javadoc.version}" windowtitle="${javadoc.windowtitle}">
+            <classpath>
+                <path path="${javac.classpath}"/>
+            </classpath>
+            <fileset dir="${src.src.dir}" excludes="${bug5101868workaround},${excludes}" includes="${includes}">
+                <filename name="**/*.java"/>
+            </fileset>
+            <fileset dir="${build.generated.sources.dir}" erroronmissingdir="false">
+                <include name="**/*.java"/>
+                <exclude name="*.java"/>
+            </fileset>
+            <arg line="${javadoc.endorsed.classpath.cmd.line.arg}"/>
+        </javadoc>
+        <copy todir="${dist.javadoc.dir}">
+            <fileset dir="${src.src.dir}" excludes="${excludes}" includes="${includes}">
+                <filename name="**/doc-files/**"/>
+            </fileset>
+            <fileset dir="${build.generated.sources.dir}" erroronmissingdir="false">
+                <include name="**/doc-files/**"/>
+            </fileset>
+        </copy>
+    </target>
+    <target depends="init,-javadoc-build" if="netbeans.home" name="-javadoc-browse" unless="no.javadoc.preview">
+        <nbbrowse file="${dist.javadoc.dir}/index.html"/>
+    </target>
+    <target depends="init,-javadoc-build,-javadoc-browse" description="Build Javadoc." name="javadoc"/>
+    <!--
+                =========================
+                TEST COMPILATION SECTION
+                =========================
+            -->
+    <target depends="init,compile" if="have.tests" name="-pre-pre-compile-test">
+        <mkdir dir="${build.test.classes.dir}"/>
+    </target>
+    <target name="-pre-compile-test">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target if="do.depend.true" name="-compile-test-depend">
+        <j2seproject3:depend classpath="${javac.test.classpath}" destdir="${build.test.classes.dir}" srcdir=""/>
+    </target>
+    <target depends="init,deps-jar,compile,-pre-pre-compile-test,-pre-compile-test,-compile-test-depend" if="have.tests" name="-do-compile-test">
+        <j2seproject3:javac apgeneratedsrcdir="${build.test.classes.dir}" classpath="${javac.test.classpath}" debug="true" destdir="${build.test.classes.dir}" processorpath="${javac.test.processorpath}" srcdir=""/>
+        <copy todir="${build.test.classes.dir}"/>
+    </target>
+    <target name="-post-compile-test">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target depends="init,compile,-pre-pre-compile-test,-pre-compile-test,-do-compile-test,-post-compile-test" name="compile-test"/>
+    <target name="-pre-compile-test-single">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target depends="init,deps-jar,compile,-pre-pre-compile-test,-pre-compile-test-single" if="have.tests" name="-do-compile-test-single">
+        <fail unless="javac.includes">Must select some files in the IDE or set javac.includes</fail>
+        <j2seproject3:force-recompile destdir="${build.test.classes.dir}"/>
+        <j2seproject3:javac apgeneratedsrcdir="${build.test.classes.dir}" classpath="${javac.test.classpath}" debug="true" destdir="${build.test.classes.dir}" excludes="" includes="${javac.includes}" processorpath="${javac.test.processorpath}" sourcepath="" srcdir=""/>
+        <copy todir="${build.test.classes.dir}"/>
+    </target>
+    <target name="-post-compile-test-single">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target depends="init,compile,-pre-pre-compile-test,-pre-compile-test-single,-do-compile-test-single,-post-compile-test-single" name="compile-test-single"/>
+    <!--
+                =======================
+                TEST EXECUTION SECTION
+                =======================
+            -->
+    <target depends="init" if="have.tests" name="-pre-test-run">
+        <mkdir dir="${build.test.results.dir}"/>
+    </target>
+    <target depends="init,compile-test,-pre-test-run" if="have.tests" name="-do-test-run">
+        <j2seproject3:test includes="${includes}" testincludes="**/*Test.java"/>
+    </target>
+    <target depends="init,compile-test,-pre-test-run,-do-test-run" if="have.tests" name="-post-test-run">
+        <fail if="tests.failed" unless="ignore.failing.tests">Some tests failed; see details above.</fail>
+    </target>
+    <target depends="init" if="have.tests" name="test-report"/>
+    <target depends="init" if="netbeans.home+have.tests" name="-test-browse"/>
+    <target depends="init,compile-test,-pre-test-run,-do-test-run,test-report,-post-test-run,-test-browse" description="Run unit tests." name="test"/>
+    <target depends="init" if="have.tests" name="-pre-test-run-single">
+        <mkdir dir="${build.test.results.dir}"/>
+    </target>
+    <target depends="init,compile-test-single,-pre-test-run-single" if="have.tests" name="-do-test-run-single">
+        <fail unless="test.includes">Must select some files in the IDE or set test.includes</fail>
+        <j2seproject3:test excludes="" includes="${test.includes}" testincludes="${test.includes}"/>
+    </target>
+    <target depends="init,compile-test-single,-pre-test-run-single,-do-test-run-single" if="have.tests" name="-post-test-run-single">
+        <fail if="tests.failed" unless="ignore.failing.tests">Some tests failed; see details above.</fail>
+    </target>
+    <target depends="init,compile-test-single,-pre-test-run-single,-do-test-run-single,-post-test-run-single" description="Run single unit test." name="test-single"/>
+    <target depends="init,compile-test-single,-pre-test-run-single" if="have.tests" name="-do-test-run-single-method">
+        <fail unless="test.class">Must select some files in the IDE or set test.class</fail>
+        <fail unless="test.method">Must select some method in the IDE or set test.method</fail>
+        <j2seproject3:test excludes="" includes="${javac.includes}" testincludes="${test.class}" testmethods="${test.method}"/>
+    </target>
+    <target depends="init,compile-test-single,-pre-test-run-single,-do-test-run-single-method" if="have.tests" name="-post-test-run-single-method">
+        <fail if="tests.failed" unless="ignore.failing.tests">Some tests failed; see details above.</fail>
+    </target>
+    <target depends="init,compile-test-single,-pre-test-run-single,-do-test-run-single-method,-post-test-run-single-method" description="Run single unit test." name="test-single-method"/>
+    <!--
+                =======================
+                TEST DEBUGGING SECTION
+                =======================
+            -->
+    <target depends="init,compile-test-single,-pre-test-run-single" if="have.tests" name="-debug-start-debuggee-test">
+        <fail unless="test.class">Must select one file in the IDE or set test.class</fail>
+        <j2seproject3:test-debug excludes="" includes="${javac.includes}" testClass="${test.class}" testincludes="${javac.includes}"/>
+    </target>
+    <target depends="init,compile-test-single,-pre-test-run-single" if="have.tests" name="-debug-start-debuggee-test-method">
+        <fail unless="test.class">Must select one file in the IDE or set test.class</fail>
+        <fail unless="test.method">Must select some method in the IDE or set test.method</fail>
+        <j2seproject3:test-debug excludes="" includes="${javac.includes}" testClass="${test.class}" testMethod="${test.method}" testincludes="${test.class}" testmethods="${test.method}"/>
+    </target>
+    <target depends="init,compile-test" if="netbeans.home+have.tests" name="-debug-start-debugger-test">
+        <j2seproject1:nbjpdastart classpath="${debug.test.classpath}" name="${test.class}"/>
+    </target>
+    <target depends="init,compile-test-single,-debug-start-debugger-test,-debug-start-debuggee-test" name="debug-test"/>
+    <target depends="init,compile-test-single,-debug-start-debugger-test,-debug-start-debuggee-test-method" name="debug-test-method"/>
+    <target depends="init,-pre-debug-fix,compile-test-single" if="netbeans.home" name="-do-debug-fix-test">
+        <j2seproject1:nbjpdareload dir="${build.test.classes.dir}"/>
+    </target>
+    <target depends="init,-pre-debug-fix,-do-debug-fix-test" if="netbeans.home" name="debug-fix-test"/>
+    <!--
+                =========================
+                APPLET EXECUTION SECTION
+                =========================
+            -->
+    <target depends="init,compile-single" name="run-applet">
+        <fail unless="applet.url">Must select one file in the IDE or set applet.url</fail>
+        <j2seproject1:java classname="sun.applet.AppletViewer">
+            <customize>
+                <arg value="${applet.url}"/>
+            </customize>
+        </j2seproject1:java>
+    </target>
+    <!--
+                =========================
+                APPLET DEBUGGING  SECTION
+                =========================
+            -->
+    <target depends="init,compile-single" if="netbeans.home" name="-debug-start-debuggee-applet">
+        <fail unless="applet.url">Must select one file in the IDE or set applet.url</fail>
+        <j2seproject3:debug classname="sun.applet.AppletViewer">
+            <customize>
+                <arg value="${applet.url}"/>
+            </customize>
+        </j2seproject3:debug>
+    </target>
+    <target depends="init,compile-single,-debug-start-debugger,-debug-start-debuggee-applet" if="netbeans.home" name="debug-applet"/>
+    <!--
+                ===============
+                CLEANUP SECTION
+                ===============
+            -->
+    <target name="-deps-clean-init" unless="built-clean.properties">
+        <property location="${build.dir}/built-clean.properties" name="built-clean.properties"/>
+        <delete file="${built-clean.properties}" quiet="true"/>
+    </target>
+    <target if="already.built.clean.${basedir}" name="-warn-already-built-clean">
+        <echo level="warn" message="Cycle detected: JNLPConverter was already built"/>
+    </target>
+    <target depends="init,-deps-clean-init" name="deps-clean" unless="no.deps">
+        <mkdir dir="${build.dir}"/>
+        <touch file="${built-clean.properties}" verbose="false"/>
+        <property file="${built-clean.properties}" prefix="already.built.clean."/>
+        <antcall target="-warn-already-built-clean"/>
+        <propertyfile file="${built-clean.properties}">
+            <entry key="${basedir}" value=""/>
+        </propertyfile>
+    </target>
+    <target depends="init" name="-do-clean">
+        <delete dir="${build.dir}"/>
+        <delete dir="${dist.dir}" followsymlinks="false" includeemptydirs="true"/>
+    </target>
+    <target name="-post-clean">
+        <!-- Empty placeholder for easier customization. -->
+        <!-- You can override this target in the ../build.xml file. -->
+    </target>
+    <target depends="init,deps-clean,-do-clean,-post-clean" description="Clean build products." name="clean"/>
+    <target name="-check-call-dep">
+        <property file="${call.built.properties}" prefix="already.built."/>
+        <condition property="should.call.dep">
+            <and>
+                <not>
+                    <isset property="already.built.${call.subproject}"/>
+                </not>
+                <available file="${call.script}"/>
+            </and>
+        </condition>
+    </target>
+    <target depends="-check-call-dep" if="should.call.dep" name="-maybe-call-dep">
+        <ant antfile="${call.script}" inheritall="false" target="${call.target}">
+            <propertyset>
+                <propertyref prefix="transfer."/>
+                <mapper from="transfer.*" to="*" type="glob"/>
+            </propertyset>
+        </ant>
+    </target>
+</project>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/nbproject/jpackage/JNLPConverter/nbproject/genfiles.properties	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,8 @@
+build.xml.data.CRC32=9017e41e
+build.xml.script.CRC32=5cf818d6
+build.xml.stylesheet.CRC32=8064a381@1.80.1.48
+# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
+# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
+nbproject/build-impl.xml.data.CRC32=9017e41e
+nbproject/build-impl.xml.script.CRC32=d0290d6d
+nbproject/build-impl.xml.stylesheet.CRC32=830a3534@1.80.1.48
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/nbproject/jpackage/JNLPConverter/nbproject/project.properties	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,96 @@
+annotation.processing.enabled=true
+annotation.processing.enabled.in.editor=false
+annotation.processing.processors.list=
+annotation.processing.run.all.processors=true
+annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output
+application.title=JNLPConverter
+application.vendor=
+auxiliary.org-netbeans-spi-editor-hints-projects.perProjectHintSettingsFile=nbproject/cfg_hints.xml
+build.classes.dir=${build.dir}/classes
+build.classes.excludes=**/*.java,**/*.form
+# This directory is removed when the project is cleaned:
+build.dir=build
+build.generated.dir=${build.dir}/generated
+build.generated.sources.dir=${build.dir}/generated-sources
+# Only compile against the classpath explicitly listed here:
+build.sysclasspath=ignore
+build.test.classes.dir=${build.dir}/test/classes
+build.test.results.dir=${build.dir}/test/results
+# Uncomment to specify the preferred debugger connection transport:
+#debug.transport=dt_socket
+debug.classpath=\
+    ${run.classpath}
+debug.test.classpath=\
+    ${run.test.classpath}
+# Files in build.classes.dir which should be excluded from distribution jar
+dist.archive.excludes=
+# This directory is removed when the project is cleaned:
+dist.dir=dist
+dist.jar=${dist.dir}/JNLPConverter.jar
+dist.javadoc.dir=${dist.dir}/javadoc
+endorsed.classpath=
+excludes=
+file.reference.jnlpconverter-src=../../../jpackage/jnlpconverter/src
+includes=**
+jar.archive.disabled=${jnlp.enabled}
+jar.compress=false
+jar.index=${jnlp.enabled}
+javac.classpath=
+# Space-separated list of extra javac options
+javac.compilerargs=
+javac.deprecation=false
+javac.external.vm=true
+javac.processorpath=\
+    ${javac.classpath}
+javac.source=1.8
+javac.target=1.8
+javac.test.classpath=\
+    ${javac.classpath}:\
+    ${build.classes.dir}
+javac.test.processorpath=\
+    ${javac.test.classpath}
+javadoc.additionalparam=
+javadoc.author=false
+javadoc.encoding=${source.encoding}
+javadoc.noindex=false
+javadoc.nonavbar=false
+javadoc.notree=false
+javadoc.private=false
+javadoc.splitindex=true
+javadoc.use=true
+javadoc.version=false
+javadoc.windowtitle=
+jnlp.codebase.type=no.codebase
+jnlp.descriptor=application
+jnlp.enabled=false
+jnlp.mixed.code=default
+jnlp.offline-allowed=false
+jnlp.signed=false
+jnlp.signing=
+jnlp.signing.alias=
+jnlp.signing.keystore=
+main.class=jnlp.converter.Main
+# Optional override of default Application-Library-Allowable-Codebase attribute identifying the locations where your signed RIA is expected to be found.
+manifest.custom.application.library.allowable.codebase=
+# Optional override of default Caller-Allowable-Codebase attribute identifying the domains from which JavaScript code can make calls to your RIA without security prompts.
+manifest.custom.caller.allowable.codebase=
+# Optional override of default Codebase manifest attribute, use to prevent RIAs from being repurposed
+manifest.custom.codebase=
+# Optional override of default Permissions manifest attribute (supported values: sandbox, all-permissions)
+manifest.custom.permissions=
+manifest.file=manifest.mf
+meta.inf.dir=${src.dir}/META-INF
+mkdist.disabled=false
+platform.active=default_platform
+run.classpath=\
+    ${javac.classpath}:\
+    ${build.classes.dir}
+# Space-separated list of JVM arguments used when running the project.
+# You may also define separate properties like run-sys-prop.name=value instead of -Dname=value.
+# To set system properties for unit tests define test-sys-prop.name=value:
+run.jvmargs=
+run.test.classpath=\
+    ${javac.test.classpath}:\
+    ${build.test.classes.dir}
+source.encoding=UTF-8
+src.src.dir=${file.reference.jnlpconverter-src}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/demo/share/nbproject/jpackage/JNLPConverter/nbproject/project.xml	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://www.netbeans.org/ns/project/1">
+    <type>org.netbeans.modules.java.j2seproject</type>
+    <configuration>
+        <data xmlns="http://www.netbeans.org/ns/j2se-project/3">
+            <name>JNLPConverter</name>
+            <source-roots>
+                <root id="src.src.dir" name="Source Packages"/>
+            </source-roots>
+            <test-roots/>
+        </data>
+    </configuration>
+</project>
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/packager/AppRuntimeImageBuilder.java	Wed Feb 27 12:19:29 2019 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,146 +0,0 @@
-/*
- * Copyright (c) 2016, 2017, Oracle and/or its affiliates. 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.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package jdk.tools.jlink.internal.packager;
-
-
-import jdk.tools.jlink.builder.DefaultImageBuilder;
-import jdk.tools.jlink.internal.Jlink;
-import jdk.tools.jlink.internal.JlinkTask;
-import jdk.tools.jlink.plugin.Plugin;
-
-import java.io.File;
-import java.io.IOException;
-import java.lang.module.ModuleFinder;
-import java.nio.ByteOrder;
-import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * AppRuntimeImageBuilder is a private API used only by the Java Packager to generate
- * a Java runtime image using jlink. AppRuntimeImageBuilder encapsulates the
- * arguments that jlink requires to generate this image. To create the image call the
- * build() method.
- */
-public final class AppRuntimeImageBuilder {
-    private Path outputDir = null;
-    private Map<String, String> launchers = Collections.emptyMap();
-    private List<Path> modulePath = null;
-    private Set<String> addModules = null;
-    private Set<String> limitModules = null;
-    private String excludeFileList = null;
-    private Map<String, String> userArguments = null;
-    private Boolean stripNativeCommands = null;
-
-    public AppRuntimeImageBuilder() {}
-
-    public void setOutputDir(Path value) {
-        outputDir = value;
-    }
-
-    public void setLaunchers(Map<String, String> value) {
-        launchers = value;
-    }
-
-    public void setModulePath(List<Path> value) {
-        modulePath = value;
-    }
-
-    public void setAddModules(Set<String> value) {
-        addModules = value;
-    }
-
-    public void setLimitModules(Set<String> value) {
-        limitModules = value;
-    }
-
-    public void setExcludeFileList(String value) {
-        excludeFileList = value;
-    }
-
-    public void setStripNativeCommands(boolean value) {
-        stripNativeCommands = value;
-    }
-
-    public void setUserArguments(Map<String, String> value) {
-        userArguments = value;
-    }
-
-    public void build() throws IOException {
-        // jlink main arguments
-        Jlink.JlinkConfiguration jlinkConfig =
-            new Jlink.JlinkConfiguration(new File("").toPath(), // Unused
-                                         addModules,
-                                         ByteOrder.nativeOrder(),
-                                         moduleFinder(modulePath,
-                                             limitModules, addModules));
-
-        // plugin configuration
-        List<Plugin> plugins = new ArrayList<Plugin>();
-
-        if (stripNativeCommands) {
-            plugins.add(Jlink.newPlugin(
-                        "strip-native-commands",
-                        Collections.singletonMap("strip-native-commands", "on"),
-                        null));
-        }
-
-        if (excludeFileList != null && !excludeFileList.isEmpty()) {
-            plugins.add(Jlink.newPlugin(
-                        "exclude-files",
-                        Collections.singletonMap("exclude-files", excludeFileList),
-                        null));
-        }
-
-        // add user supplied jlink arguments
-        for (Map.Entry<String, String> entry : userArguments.entrySet()) {
-            String key = entry.getKey();
-            String value = entry.getValue();
-            plugins.add(Jlink.newPlugin(key,
-                                        Collections.singletonMap(key, value),
-                                        null));
-        }
-
-        // build the image
-        Jlink.PluginsConfiguration pluginConfig = new Jlink.PluginsConfiguration(
-            plugins, new DefaultImageBuilder(outputDir, launchers), null);
-        Jlink jlink = new Jlink();
-        jlink.build(jlinkConfig, pluginConfig);
-    }
-
-    /*
-     * Returns a ModuleFinder that limits observability to the given root
-     * modules, their transitive dependences, plus a set of other modules.
-     */
-    public static ModuleFinder moduleFinder(List<Path> modulepaths,
-                                            Set<String> roots,
-                                            Set<String> otherModules) {
-        return JlinkTask.newModuleFinder(modulepaths, roots, otherModules);
-    }
-}
--- a/src/jdk.jlink/share/classes/module-info.java	Wed Feb 27 12:19:29 2019 -0800
+++ b/src/jdk.jlink/share/classes/module-info.java	Wed Feb 27 20:45:51 2019 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2019, Oracle and/or its affiliates. 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
@@ -77,4 +77,4 @@
         jdk.tools.jlink.internal.plugins.IncludeLocalesPlugin,
         jdk.tools.jlink.internal.plugins.GenerateJLIClassesPlugin,
         jdk.tools.jlink.internal.plugins.ReleaseInfoPlugin;
- }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxAppBundler.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,234 @@
+/*
+ * Copyright (c) 2012, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jpackage.internal;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.text.MessageFormat;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Map;
+import java.util.ResourceBundle;
+
+import static jdk.jpackage.internal.StandardBundlerParam.*;
+
+public class LinuxAppBundler extends AbstractImageBundler {
+
+    private static final ResourceBundle I18N = ResourceBundle.getBundle(
+            "jdk.jpackage.internal.resources.LinuxResources");
+
+    public static final BundlerParamInfo<File> ICON_PNG =
+            new StandardBundlerParam<>(
+            I18N.getString("param.icon-png.name"),
+            I18N.getString("param.icon-png.description"),
+            "icon.png",
+            File.class,
+            params -> {
+                File f = ICON.fetchFrom(params);
+                if (f != null && !f.getName().toLowerCase().endsWith(".png")) {
+                    Log.error(MessageFormat.format(
+                            I18N.getString("message.icon-not-png"), f));
+                    return null;
+                }
+                return f;
+            },
+            (s, p) -> new File(s));
+
+    public static final BundlerParamInfo<String> LINUX_INSTALL_DIR =
+            new StandardBundlerParam<>(
+            I18N.getString("param.linux-install-dir.name"),
+            I18N.getString("param.linux-install-dir.description"),
+            "linux-install-dir",
+            String.class,
+            params -> {
+                 String dir = INSTALL_DIR.fetchFrom(params);
+                 if (dir != null) {
+                     if (dir.endsWith("/")) {
+                         dir = dir.substring(0, dir.length()-1);
+                     }
+                     return dir;
+                 }
+                 return "/opt";
+             },
+            (s, p) -> s
+    );
+
+    public static final BundlerParamInfo<String> LINUX_PACKAGE_DEPENDENCIES =
+            new StandardBundlerParam<>(
+            I18N.getString("param.linux-package-dependencies.name"),
+            I18N.getString("param.linux-package-dependencies.description"),
+            Arguments.CLIOptions.LINUX_PACKAGE_DEPENDENCIES.getId(),
+            String.class,
+            params -> {
+                 return "";
+             },
+            (s, p) -> s
+    );
+
+    @Override
+    public boolean validate(Map<String, ? super Object> p)
+            throws UnsupportedPlatformException, ConfigException {
+        try {
+            if (p == null) throw new ConfigException(
+                    I18N.getString("error.parameters-null"),
+                    I18N.getString("error.parameters-null.advice"));
+
+            return doValidate(p);
+        } catch (RuntimeException re) {
+            if (re.getCause() instanceof ConfigException) {
+                throw (ConfigException) re.getCause();
+            } else {
+                throw new ConfigException(re);
+            }
+        }
+    }
+
+    private boolean doValidate(Map<String, ? super Object> p)
+            throws UnsupportedPlatformException, ConfigException {
+        if (Platform.getPlatform() != Platform.LINUX) {
+            throw new UnsupportedPlatformException();
+        }
+
+        imageBundleValidation(p);
+
+        return true;
+    }
+
+    // it is static for the sake of sharing with "installer" bundlers
+    // that may skip calls to validate/bundle in this class!
+    public static File getRootDir(File outDir, Map<String, ? super Object> p) {
+        return new File(outDir, APP_NAME.fetchFrom(p));
+    }
+
+    public static String getLauncherCfgName(Map<String, ? super Object> p) {
+        return "app/" + APP_NAME.fetchFrom(p) +".cfg";
+    }
+
+    File doBundle(Map<String, ? super Object> p, File outputDirectory,
+            boolean dependentTask) throws PackagerException {
+        if (RUNTIME_INSTALLER.fetchFrom(p)) {
+            return doJreBundle(p, outputDirectory, dependentTask);
+        } else {
+            return doAppBundle(p, outputDirectory, dependentTask);
+        }
+    }
+
+    private File doJreBundle(Map<String, ? super Object> p,
+            File outputDirectory, boolean dependentTask) throws PackagerException {
+        try {
+            File rootDirectory = createRoot(p, outputDirectory, dependentTask,
+                    APP_NAME.fetchFrom(p), "linuxapp-image-builder");
+            AbstractAppImageBuilder appBuilder = new LinuxAppImageBuilder(
+                    APP_NAME.fetchFrom(p), outputDirectory.toPath());
+            File predefined = PREDEFINED_RUNTIME_IMAGE.fetchFrom(p);
+            if (predefined == null ) {
+                JLinkBundlerHelper.generateJre(p, appBuilder);
+            } else {
+                return predefined;
+            }
+            return rootDirectory;
+        } catch (PackagerException pe) {
+            throw pe;
+        } catch (Exception ex) {
+            Log.verbose(ex);
+            throw new PackagerException(ex);
+        }
+    }
+
+    private File doAppBundle(Map<String, ? super Object> p,
+            File outputDirectory, boolean dependentTask) throws PackagerException {
+        try {
+            File rootDirectory = createRoot(p, outputDirectory, dependentTask,
+                    APP_NAME.fetchFrom(p), "linuxapp-image-builder");
+            AbstractAppImageBuilder appBuilder = new LinuxAppImageBuilder(p,
+                    outputDirectory.toPath());
+            if (PREDEFINED_RUNTIME_IMAGE.fetchFrom(p) == null ) {
+                JLinkBundlerHelper.execute(p, appBuilder);
+            } else {
+                StandardBundlerParam.copyPredefinedRuntimeImage(p, appBuilder);
+            }
+            return rootDirectory;
+        } catch (PackagerException pe) {
+            throw pe;
+        } catch (Exception ex) {
+            Log.verbose(ex);
+            throw new PackagerException(ex);
+        }
+    }
+
+    @Override
+    public String getName() {
+        return I18N.getString("app.bundler.name");
+    }
+
+    @Override
+    public String getDescription() {
+        return I18N.getString("app.bundler.description");
+    }
+
+    @Override
+    public String getID() {
+        return "linux.app";
+    }
+
+    @Override
+    public String getBundleType() {
+        return "IMAGE";
+    }
+
+    @Override
+    public Collection<BundlerParamInfo<?>> getBundleParameters() {
+        return getAppBundleParameters();
+    }
+
+    public static Collection<BundlerParamInfo<?>> getAppBundleParameters() {
+        return Arrays.asList(
+                APP_NAME,
+                APP_RESOURCES,
+                ARGUMENTS,
+                CLASSPATH,
+                JVM_OPTIONS,
+                MAIN_CLASS,
+                MAIN_JAR,
+                PREFERENCES_ID,
+                VERSION,
+                VERBOSE
+        );
+    }
+
+    @Override
+    public File execute(Map<String, ? super Object> params,
+            File outputParentDir) throws PackagerException {
+        return doBundle(params, outputParentDir, false);
+    }
+
+    @Override
+    public boolean supported(boolean runtimeInstaller) {
+        return (Platform.getPlatform() == Platform.LINUX);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxAppImageBuilder.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,251 @@
+/*
+ * Copyright (c) 2015, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jpackage.internal;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.UncheckedIOException;
+import java.io.Writer;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.attribute.PosixFilePermission;
+import java.text.MessageFormat;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.ResourceBundle;
+import java.util.Set;
+
+import static jdk.jpackage.internal.StandardBundlerParam.*;
+
+public class LinuxAppImageBuilder extends AbstractAppImageBuilder {
+
+    private static final ResourceBundle I18N = ResourceBundle.getBundle(
+        "jdk.jpackage.internal.resources.LinuxResources");
+
+    private static final String LIBRARY_NAME = "libapplauncher.so";
+
+    private final Path root;
+    private final Path appDir;
+    private final Path appModsDir;
+    private final Path runtimeDir;
+    private final Path resourcesDir;
+    private final Path mdir;
+
+    private final Map<String, ? super Object> params;
+
+    public static final BundlerParamInfo<File> ICON_PNG =
+            new StandardBundlerParam<>(
+            I18N.getString("param.icon-png.name"),
+            I18N.getString("param.icon-png.description"),
+            "icon.png",
+            File.class,
+            params -> {
+                File f = ICON.fetchFrom(params);
+                if (f != null && !f.getName().toLowerCase().endsWith(".png")) {
+                    Log.error(MessageFormat.format(I18N.getString(
+                            "message.icon-not-png"), f));
+                    return null;
+                }
+                return f;
+            },
+            (s, p) -> new File(s));
+
+    public LinuxAppImageBuilder(Map<String, Object> config, Path imageOutDir)
+            throws IOException {
+        super(config,
+                imageOutDir.resolve(APP_NAME.fetchFrom(config) + "/runtime"));
+
+        Objects.requireNonNull(imageOutDir);
+
+        this.root = imageOutDir.resolve(APP_NAME.fetchFrom(config));
+        this.appDir = root.resolve("app");
+        this.appModsDir = appDir.resolve("mods");
+        this.runtimeDir = root.resolve("runtime");
+        this.resourcesDir = root.resolve("resources");
+        this.mdir = runtimeDir.resolve("lib");
+        this.params = new HashMap<>();
+        config.entrySet().stream().forEach(e -> params.put(
+                e.getKey().toString(), e.getValue()));
+        Files.createDirectories(appDir);
+        Files.createDirectories(runtimeDir);
+        Files.createDirectories(resourcesDir);
+    }
+
+    public LinuxAppImageBuilder(String appName, Path imageOutDir)
+            throws IOException {
+        super(null, imageOutDir.resolve(appName));
+
+        Objects.requireNonNull(imageOutDir);
+
+        this.root = imageOutDir.resolve(appName);
+        this.appDir = null;
+        this.appModsDir = null;
+        this.runtimeDir = null;
+        this.resourcesDir = null;
+        this.mdir = null;
+        this.params = new HashMap<>();
+    }
+
+    private Path destFile(String dir, String filename) {
+        return runtimeDir.resolve(dir).resolve(filename);
+    }
+
+    private void writeEntry(InputStream in, Path dstFile) throws IOException {
+        Files.createDirectories(dstFile.getParent());
+        Files.copy(in, dstFile);
+    }
+
+    private void writeSymEntry(Path dstFile, Path target) throws IOException {
+        Files.createDirectories(dstFile.getParent());
+        Files.createLink(dstFile, target);
+    }
+
+    /**
+     * chmod ugo+x file
+     */
+    private void setExecutable(Path file) {
+        try {
+            Set<PosixFilePermission> perms =
+                    Files.getPosixFilePermissions(file);
+            perms.add(PosixFilePermission.OWNER_EXECUTE);
+            perms.add(PosixFilePermission.GROUP_EXECUTE);
+            perms.add(PosixFilePermission.OTHERS_EXECUTE);
+            Files.setPosixFilePermissions(file, perms);
+        } catch (IOException ioe) {
+            throw new UncheckedIOException(ioe);
+        }
+    }
+
+    private static void createUtf8File(File file, String content)
+            throws IOException {
+        try (OutputStream fout = new FileOutputStream(file);
+            Writer output = new OutputStreamWriter(fout, "UTF-8")) {
+            output.write(content);
+        }
+    }
+
+
+    // it is static for the sake of sharing with "installer" bundlers
+    // that may skip calls to validate/bundle in this class!
+    public static File getRootDir(File outDir, Map<String, ? super Object> p) {
+        return new File(outDir, APP_NAME.fetchFrom(p));
+    }
+
+    public static String getLauncherName(Map<String, ? super Object> p) {
+        return APP_NAME.fetchFrom(p);
+    }
+
+    public static String getLauncherCfgName(Map<String, ? super Object> p) {
+        return "app/" + APP_NAME.fetchFrom(p) + ".cfg";
+    }
+
+    @Override
+    public Path getAppDir() {
+        return appDir;
+    }
+
+    @Override
+    public Path getAppModsDir() {
+        return appModsDir;
+    }
+
+    @Override
+    public void prepareApplicationFiles() throws IOException {
+        Map<String, ? super Object> originalParams = new HashMap<>(params);
+
+        // create the primary launcher
+        createLauncherForEntryPoint(params, root);
+
+        // Copy library to the launcher folder
+        try (InputStream is_lib = getResourceAsStream(LIBRARY_NAME)) {
+            writeEntry(is_lib, root.resolve(LIBRARY_NAME));
+        }
+
+        // create the secondary launchers, if any
+        List<Map<String, ? super Object>> entryPoints
+                = StandardBundlerParam.SECONDARY_LAUNCHERS.fetchFrom(params);
+        for (Map<String, ? super Object> entryPoint : entryPoints) {
+            Map<String, ? super Object> tmp = new HashMap<>(originalParams);
+            tmp.putAll(entryPoint);
+            createLauncherForEntryPoint(tmp, root);
+        }
+
+        // Copy class path entries to Java folder
+        copyApplication();
+
+        // Copy icon to Resources folder
+        copyIcon();
+    }
+
+    @Override
+    public void prepareJreFiles() throws IOException {}
+
+    private void createLauncherForEntryPoint(Map<String, ? super Object> p,
+            Path rootDir) throws IOException {
+        // Copy executable to Linux folder
+        Path executableFile = root.resolve(getLauncherName(p));
+        try (InputStream is_launcher =
+                getResourceAsStream("jpackageapplauncher")) {
+            writeEntry(is_launcher, executableFile);
+        }
+
+        executableFile.toFile().setExecutable(true, false);
+        executableFile.toFile().setWritable(true, true);
+
+        writeCfgFile(p, root.resolve(getLauncherCfgName(p)).toFile(),
+                "$APPDIR/runtime");
+    }
+
+    private void copyIcon() throws IOException {
+        File icon = ICON_PNG.fetchFrom(params);
+        if (icon != null) {
+            File iconTarget = new File(resourcesDir.toFile(),
+                    APP_NAME.fetchFrom(params) + ".png");
+            IOUtils.copyFile(icon, iconTarget);
+        }
+    }
+
+    private void copyApplication() throws IOException {
+        for (RelativeFileSet appResources :
+                APP_RESOURCES_LIST.fetchFrom(params)) {
+            if (appResources == null) {
+                throw new RuntimeException("Null app resources?");
+            }
+            File srcdir = appResources.getBaseDirectory();
+            for (String fname : appResources.getIncludedFiles()) {
+                copyEntry(appDir, srcdir, fname);
+            }
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebBundler.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,894 @@
+/*
+ * Copyright (c) 2012, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jpackage.internal;
+
+import javax.imageio.ImageIO;
+import java.awt.image.BufferedImage;
+import java.io.*;
+import java.nio.file.Files;
+import java.nio.file.attribute.PosixFilePermission;
+import java.nio.file.attribute.PosixFilePermissions;
+import java.text.MessageFormat;
+import java.util.*;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.regex.Pattern;
+
+import static jdk.jpackage.internal.StandardBundlerParam.*;
+import static jdk.jpackage.internal.LinuxAppBundler.ICON_PNG;
+import static jdk.jpackage.internal.LinuxAppBundler.LINUX_INSTALL_DIR;
+import static jdk.jpackage.internal.LinuxAppBundler.LINUX_PACKAGE_DEPENDENCIES;
+
+public class LinuxDebBundler extends AbstractBundler {
+
+    private static final ResourceBundle I18N = ResourceBundle.getBundle(
+                    "jdk.jpackage.internal.resources.LinuxResources");
+
+    public static final BundlerParamInfo<LinuxAppBundler> APP_BUNDLER =
+            new StandardBundlerParam<>(
+            I18N.getString("param.deb-app-bundler.name"),
+            I18N.getString("param.deb-app-bundler.description"),
+            "linux.app.bundler",
+            LinuxAppBundler.class,
+            params -> new LinuxAppBundler(),
+            (s, p) -> null);
+
+    // Debian rules for package naming are used here
+    // https://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-Source
+    //
+    // Package names must consist only of lower case letters (a-z),
+    // digits (0-9), plus (+) and minus (-) signs, and periods (.).
+    // They must be at least two characters long and
+    // must start with an alphanumeric character.
+    //
+    private static final Pattern DEB_BUNDLE_NAME_PATTERN =
+            Pattern.compile("^[a-z][a-z\\d\\+\\-\\.]+");
+
+    public static final BundlerParamInfo<String> BUNDLE_NAME =
+            new StandardBundlerParam<> (
+            I18N.getString("param.bundle-name.name"),
+            I18N.getString("param.bundle-name.description"),
+            Arguments.CLIOptions.LINUX_BUNDLE_NAME.getId(),
+            String.class,
+            params -> {
+                String nm = APP_NAME.fetchFrom(params);
+
+                if (nm == null) return null;
+
+                // make sure to lower case and spaces/underscores become dashes
+                nm = nm.toLowerCase().replaceAll("[ _]", "-");
+                return nm;
+            },
+            (s, p) -> {
+                if (!DEB_BUNDLE_NAME_PATTERN.matcher(s).matches()) {
+                    throw new IllegalArgumentException(new ConfigException(
+                            MessageFormat.format(I18N.getString(
+                            "error.invalid-value-for-package-name"), s),
+                            I18N.getString(
+                            "error.invalid-value-for-package-name.advice")));
+                }
+
+                return s;
+            });
+
+    public static final BundlerParamInfo<String> FULL_PACKAGE_NAME =
+            new StandardBundlerParam<> (
+            I18N.getString("param.full-package-name.name"),
+            I18N.getString("param.full-package-name.description"),
+            "linux.deb.fullPackageName",
+            String.class,
+            params -> BUNDLE_NAME.fetchFrom(params) + "-"
+                    + VERSION.fetchFrom(params),
+            (s, p) -> s);
+
+    public static final BundlerParamInfo<File> DEB_IMAGE_DIR =
+            new StandardBundlerParam<>(
+            I18N.getString("param.image-dir.name"),
+            I18N.getString("param.image-dir.description"),
+            "linux.deb.imageDir",
+            File.class,
+            params -> {
+                File imagesRoot = IMAGES_ROOT.fetchFrom(params);
+                if (!imagesRoot.exists()) imagesRoot.mkdirs();
+                return new File(new File(imagesRoot, "linux-deb.image"),
+                        FULL_PACKAGE_NAME.fetchFrom(params));
+            },
+            (s, p) -> new File(s));
+
+    public static final BundlerParamInfo<File> APP_IMAGE_ROOT =
+            new StandardBundlerParam<>(
+            I18N.getString("param.app-image-root.name"),
+            I18N.getString("param.app-image-root.description"),
+            "linux.deb.imageRoot",
+            File.class,
+            params -> {
+                File imageDir = DEB_IMAGE_DIR.fetchFrom(params);
+                return new File(imageDir, LINUX_INSTALL_DIR.fetchFrom(params));
+            },
+            (s, p) -> new File(s));
+
+    public static final BundlerParamInfo<File> CONFIG_DIR =
+            new StandardBundlerParam<>(
+            I18N.getString("param.config-dir.name"),
+            I18N.getString("param.config-dir.description"),
+            "linux.deb.configDir",
+            File.class,
+            params ->  new File(DEB_IMAGE_DIR.fetchFrom(params), "DEBIAN"),
+            (s, p) -> new File(s));
+
+    public static final BundlerParamInfo<String> EMAIL =
+            new StandardBundlerParam<> (
+            I18N.getString("param.maintainer-email.name"),
+            I18N.getString("param.maintainer-email.description"),
+            BundleParams.PARAM_EMAIL,
+            String.class,
+            params -> "Unknown",
+            (s, p) -> s);
+
+    public static final BundlerParamInfo<String> MAINTAINER =
+            new StandardBundlerParam<> (
+            I18N.getString("param.maintainer-name.name"),
+            I18N.getString("param.maintainer-name.description"),
+            Arguments.CLIOptions.LINUX_DEB_MAINTAINER.getId(),
+            String.class,
+            params -> VENDOR.fetchFrom(params) + " <"
+                    + EMAIL.fetchFrom(params) + ">",
+            (s, p) -> s);
+
+    public static final BundlerParamInfo<String> LICENSE_TEXT =
+            new StandardBundlerParam<> (
+            I18N.getString("param.license-text.name"),
+            I18N.getString("param.license-text.description"),
+            "linux.deb.licenseText",
+            String.class,
+            params -> {
+                try {
+                    String licenseFile = LICENSE_FILE.fetchFrom(params);
+                    if (licenseFile != null) {
+                        return Files.readString(new File(licenseFile).toPath());
+                    }
+                } catch (Exception e) {
+                    Log.verbose(e);
+                }
+                return "Unknown";
+            },
+            (s, p) -> s);
+
+    public static final BundlerParamInfo<String> XDG_FILE_PREFIX =
+            new StandardBundlerParam<> (
+            I18N.getString("param.xdg-prefix.name"),
+            I18N.getString("param.xdg-prefix.description"),
+            "linux.xdg-prefix",
+            String.class,
+            params -> {
+                try {
+                    String vendor;
+                    if (params.containsKey(VENDOR.getID())) {
+                        vendor = VENDOR.fetchFrom(params);
+                    } else {
+                        vendor = "jpackage";
+                    }
+                    String appName = APP_NAME.fetchFrom(params);
+
+                    return (appName + "-" + vendor).replaceAll("\\s", "");
+                } catch (Exception e) {
+                    Log.verbose(e);
+                }
+                return "unknown-MimeInfo.xml";
+            },
+            (s, p) -> s);
+
+    private final static String DEFAULT_ICON = "javalogo_white_32.png";
+    private final static String DEFAULT_CONTROL_TEMPLATE = "template.control";
+    private final static String DEFAULT_PRERM_TEMPLATE = "template.prerm";
+    private final static String DEFAULT_PREINSTALL_TEMPLATE =
+            "template.preinst";
+    private final static String DEFAULT_POSTRM_TEMPLATE = "template.postrm";
+    private final static String DEFAULT_POSTINSTALL_TEMPLATE =
+            "template.postinst";
+    private final static String DEFAULT_COPYRIGHT_TEMPLATE =
+            "template.copyright";
+    private final static String DEFAULT_DESKTOP_FILE_TEMPLATE =
+            "template.desktop";
+
+    public final static String TOOL_DPKG = "dpkg-deb";
+
+    public static boolean testTool(String toolName, String minVersion) {
+        try {
+            ProcessBuilder pb = new ProcessBuilder(
+                    toolName,
+                    "--version");
+            // not interested in the output
+            IOUtils.exec(pb, Log.isDebug(), true);
+        } catch (Exception e) {
+            Log.verbose(MessageFormat.format(I18N.getString(
+                    "message.test-for-tool"), toolName, e.getMessage()));
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public boolean validate(Map<String, ? super Object> p)
+            throws UnsupportedPlatformException, ConfigException {
+        try {
+            if (p == null) throw new ConfigException(
+                    I18N.getString("error.parameters-null"),
+                    I18N.getString("error.parameters-null.advice"));
+
+            //run basic validation to ensure requirements are met
+            //we are not interested in return code, only possible exception
+            APP_BUNDLER.fetchFrom(p).validate(p);
+
+            // NOTE: Can we validate that the required tools are available
+            // before we start?
+            if (!testTool(TOOL_DPKG, "1")){
+                throw new ConfigException(MessageFormat.format(
+                        I18N.getString("error.tool-not-found"), TOOL_DPKG),
+                        I18N.getString("error.tool-not-found.advice"));
+            }
+
+
+            // Show warning is license file is missing
+            String licenseFile = LICENSE_FILE.fetchFrom(p);
+            if (licenseFile == null) {
+                Log.verbose(I18N.getString("message.debs-like-licenses"));
+            }
+
+            // only one mime type per association, at least one file extention
+            List<Map<String, ? super Object>> associations =
+                    FILE_ASSOCIATIONS.fetchFrom(p);
+            if (associations != null) {
+                for (int i = 0; i < associations.size(); i++) {
+                    Map<String, ? super Object> assoc = associations.get(i);
+                    List<String> mimes = FA_CONTENT_TYPE.fetchFrom(assoc);
+                    if (mimes == null || mimes.isEmpty()) {
+                        String msgKey =
+                            "error.no-content-types-for-file-association";
+                        throw new ConfigException(
+                                MessageFormat.format(I18N.getString(msgKey), i),
+                                I18N.getString(msgKey + ".advise"));
+
+                    } else if (mimes.size() > 1) {
+                        String msgKey =
+                            "error.too-many-content-types-for-file-association";
+                        throw new ConfigException(
+                                MessageFormat.format(I18N.getString(msgKey), i),
+                                I18N.getString(msgKey + ".advise"));
+                    }
+                }
+            }
+
+            // bundle name has some restrictions
+            // the string converter will throw an exception if invalid
+            BUNDLE_NAME.getStringConverter().apply(BUNDLE_NAME.fetchFrom(p), p);
+
+            return true;
+        } catch (RuntimeException re) {
+            if (re.getCause() instanceof ConfigException) {
+                throw (ConfigException) re.getCause();
+            } else {
+                throw new ConfigException(re);
+            }
+        }
+    }
+
+    private boolean prepareProto(Map<String, ? super Object> p)
+            throws PackagerException, IOException {
+        File appImage = StandardBundlerParam.getPredefinedAppImage(p);
+        File appDir = null;
+
+        // we either have an application image or need to build one
+        if (appImage != null) {
+            appDir = new File(APP_IMAGE_ROOT.fetchFrom(p),
+                APP_NAME.fetchFrom(p));
+            // copy everything from appImage dir into appDir/name
+            IOUtils.copyRecursive(appImage.toPath(), appDir.toPath());
+        } else {
+            appDir = APP_BUNDLER.fetchFrom(p).doBundle(p,
+                    APP_IMAGE_ROOT.fetchFrom(p), true);
+        }
+        return appDir != null;
+    }
+
+    //@Override
+    public File bundle(Map<String, ? super Object> p,
+            File outdir) throws PackagerException {
+        if (!outdir.isDirectory() && !outdir.mkdirs()) {
+            throw new PackagerException ("error.cannot-create-output-dir",
+                    outdir.getAbsolutePath());
+        }
+        if (!outdir.canWrite()) {
+            throw new PackagerException("error.cannot-write-to-output-dir",
+                    outdir.getAbsolutePath());
+        }
+
+        // we want to create following structure
+        //   <package-name>
+        //        DEBIAN
+        //          control   (file with main package details)
+        //          menu      (request to create menu)
+        //          ... other control files if needed ....
+        //        opt  (by default)
+        //          AppFolder (this is where app image goes)
+        //             launcher executable
+        //             app
+        //             runtime
+
+        File imageDir = DEB_IMAGE_DIR.fetchFrom(p);
+        File configDir = CONFIG_DIR.fetchFrom(p);
+
+        try {
+
+            imageDir.mkdirs();
+            configDir.mkdirs();
+            if (prepareProto(p) && prepareProjectConfig(p)) {
+                return buildDeb(p, outdir);
+            }
+            return null;
+        } catch (IOException ex) {
+            Log.verbose(ex);
+            throw new PackagerException(ex);
+        }
+    }
+
+    /*
+     * set permissions with a string like "rwxr-xr-x"
+     *
+     * This cannot be directly backport to 22u which is built with 1.6
+     */
+    private void setPermissions(File file, String permissions) {
+        Set<PosixFilePermission> filePermissions =
+                PosixFilePermissions.fromString(permissions);
+        try {
+            if (file.exists()) {
+                Files.setPosixFilePermissions(file.toPath(), filePermissions);
+            }
+        } catch (IOException ex) {
+            Logger.getLogger(LinuxDebBundler.class.getName()).log(
+                    Level.SEVERE, null, ex);
+        }
+
+    }
+
+    private String getArch() {
+        String arch = System.getProperty("os.arch");
+        if ("i386".equals(arch))
+            return "i386";
+        else
+            return "amd64";
+    }
+
+    private long getInstalledSizeKB(Map<String, ? super Object> params) {
+        return getInstalledSizeKB(APP_IMAGE_ROOT.fetchFrom(params)) >> 10;
+    }
+
+    private long getInstalledSizeKB(File dir) {
+        long count = 0;
+        File[] children = dir.listFiles();
+        if (children != null) {
+            for (File file : children) {
+                if (file.isFile()) {
+                    count += file.length();
+                }
+                else if (file.isDirectory()) {
+                    count += getInstalledSizeKB(file);
+                }
+            }
+        }
+        return count;
+    }
+
+    private boolean prepareProjectConfig(Map<String, ? super Object> params)
+            throws IOException {
+        Map<String, String> data = createReplacementData(params);
+        File rootDir = LinuxAppBundler.getRootDir(APP_IMAGE_ROOT.fetchFrom(
+                params), params);
+
+        File iconTarget = getConfig_IconFile(rootDir, params);
+        File icon = ICON_PNG.fetchFrom(params);
+        if (!RUNTIME_INSTALLER.fetchFrom(params)) {
+            // prepare installer icon
+            if (icon == null || !icon.exists()) {
+                fetchResource(iconTarget.getName(),
+                        I18N.getString("resource.menu-icon"),
+                        DEFAULT_ICON,
+                        iconTarget,
+                        VERBOSE.fetchFrom(params),
+                        RESOURCE_DIR.fetchFrom(params));
+            } else {
+                fetchResource(iconTarget.getName(),
+                        I18N.getString("resource.menu-icon"),
+                        icon,
+                        iconTarget,
+                        VERBOSE.fetchFrom(params),
+                        RESOURCE_DIR.fetchFrom(params));
+            }
+        }
+
+        StringBuilder installScripts = new StringBuilder();
+        StringBuilder removeScripts = new StringBuilder();
+        for (Map<String, ? super Object> secondaryLauncher :
+                SECONDARY_LAUNCHERS.fetchFrom(params)) {
+            Map<String, String> secondaryLauncherData =
+                    createReplacementData(secondaryLauncher);
+            secondaryLauncherData.put("APPLICATION_FS_NAME",
+                    data.get("APPLICATION_FS_NAME"));
+            secondaryLauncherData.put("DESKTOP_MIMES", "");
+
+            if (!RUNTIME_INSTALLER.fetchFrom(params)) {
+                // prepare desktop shortcut
+                Writer w = new BufferedWriter(new FileWriter(
+                        getConfig_DesktopShortcutFile(
+                                rootDir, secondaryLauncher)));
+                String content = preprocessTextResource(
+                        getConfig_DesktopShortcutFile(rootDir,
+                        secondaryLauncher).getName(),
+                        I18N.getString("resource.menu-shortcut-descriptor"),
+                        DEFAULT_DESKTOP_FILE_TEMPLATE,
+                        secondaryLauncherData,
+                        VERBOSE.fetchFrom(params),
+                        RESOURCE_DIR.fetchFrom(params));
+                w.write(content);
+                w.close();
+            }
+
+            // prepare installer icon
+            iconTarget = getConfig_IconFile(rootDir, secondaryLauncher);
+            icon = ICON_PNG.fetchFrom(secondaryLauncher);
+            if (icon == null || !icon.exists()) {
+                fetchResource(iconTarget.getName(),
+                        I18N.getString("resource.menu-icon"),
+                        DEFAULT_ICON,
+                        iconTarget,
+                        VERBOSE.fetchFrom(params),
+                        RESOURCE_DIR.fetchFrom(params));
+            } else {
+                fetchResource(iconTarget.getName(),
+                        I18N.getString("resource.menu-icon"),
+                        icon,
+                        iconTarget,
+                        VERBOSE.fetchFrom(params),
+                        RESOURCE_DIR.fetchFrom(params));
+            }
+
+            // postinst copying of desktop icon
+            installScripts.append(
+                    "        xdg-desktop-menu install --novendor ");
+            installScripts.append(LINUX_INSTALL_DIR.fetchFrom(params));
+            installScripts.append("/");
+            installScripts.append(data.get("APPLICATION_FS_NAME"));
+            installScripts.append("/");
+            installScripts.append(
+                    secondaryLauncherData.get("APPLICATION_LAUNCHER_FILENAME"));
+            installScripts.append(".desktop\n");
+
+            // postrm cleanup of desktop icon
+            removeScripts.append(
+                    "        xdg-desktop-menu uninstall --novendor ");
+            removeScripts.append(LINUX_INSTALL_DIR.fetchFrom(params));
+            removeScripts.append("/");
+            removeScripts.append(data.get("APPLICATION_FS_NAME"));
+            removeScripts.append("/");
+            removeScripts.append(
+                    secondaryLauncherData.get("APPLICATION_LAUNCHER_FILENAME"));
+            removeScripts.append(".desktop\n");
+        }
+        data.put("SECONDARY_LAUNCHERS_INSTALL", installScripts.toString());
+        data.put("SECONDARY_LAUNCHERS_REMOVE", removeScripts.toString());
+
+        List<Map<String, ? super Object>> associations =
+                FILE_ASSOCIATIONS.fetchFrom(params);
+        data.put("FILE_ASSOCIATION_INSTALL", "");
+        data.put("FILE_ASSOCIATION_REMOVE", "");
+        data.put("DESKTOP_MIMES", "");
+        if (associations != null) {
+            String mimeInfoFile = XDG_FILE_PREFIX.fetchFrom(params)
+                    + "-MimeInfo.xml";
+            StringBuilder mimeInfo = new StringBuilder(
+                "<?xml version=\"1.0\"?>\n<mime-info xmlns="
+                + "'http://www.freedesktop.org/standards/shared-mime-info'>\n");
+            StringBuilder registrations = new StringBuilder();
+            StringBuilder deregistrations = new StringBuilder();
+            StringBuilder desktopMimes = new StringBuilder("MimeType=");
+            boolean addedEntry = false;
+
+            for (Map<String, ? super Object> assoc : associations) {
+                //  <mime-type type="application/x-vnd.awesome">
+                //    <comment>Awesome document</comment>
+                //    <glob pattern="*.awesome"/>
+                //    <glob pattern="*.awe"/>
+                //  </mime-type>
+
+                if (assoc == null) {
+                    continue;
+                }
+
+                String description = FA_DESCRIPTION.fetchFrom(assoc);
+                File faIcon = FA_ICON.fetchFrom(assoc);
+                List<String> extensions = FA_EXTENSIONS.fetchFrom(assoc);
+                if (extensions == null) {
+                    Log.error(I18N.getString(
+                          "message.creating-association-with-null-extension"));
+                }
+
+                List<String> mimes = FA_CONTENT_TYPE.fetchFrom(assoc);
+                if (mimes == null || mimes.isEmpty()) {
+                    continue;
+                }
+                String thisMime = mimes.get(0);
+                String dashMime = thisMime.replace('/', '-');
+
+                mimeInfo.append("  <mime-type type='")
+                        .append(thisMime)
+                        .append("'>\n");
+                if (description != null && !description.isEmpty()) {
+                    mimeInfo.append("    <comment>")
+                            .append(description)
+                            .append("</comment>\n");
+                }
+
+                if (extensions != null) {
+                    for (String ext : extensions) {
+                        mimeInfo.append("    <glob pattern='*.")
+                                .append(ext)
+                                .append("'/>\n");
+                    }
+                }
+
+                mimeInfo.append("  </mime-type>\n");
+                if (!addedEntry) {
+                    registrations.append("        xdg-mime install ")
+                            .append(LINUX_INSTALL_DIR.fetchFrom(params))
+                            .append("/")
+                            .append(data.get("APPLICATION_FS_NAME"))
+                            .append("/")
+                            .append(mimeInfoFile)
+                            .append("\n");
+
+                    deregistrations.append("        xdg-mime uninstall ")
+                            .append(LINUX_INSTALL_DIR.fetchFrom(params))
+                            .append("/")
+                            .append(data.get("APPLICATION_FS_NAME"))
+                            .append("/")
+                            .append(mimeInfoFile)
+                            .append("\n");
+                    addedEntry = true;
+                } else {
+                    desktopMimes.append(";");
+                }
+                desktopMimes.append(thisMime);
+
+                if (faIcon != null && faIcon.exists()) {
+                    int size = getSquareSizeOfImage(faIcon);
+
+                    if (size > 0) {
+                        File target = new File(rootDir,
+                                APP_NAME.fetchFrom(params)
+                                + "_fa_" + faIcon.getName());
+                        IOUtils.copyFile(faIcon, target);
+
+                        // xdg-icon-resource install --context mimetypes
+                        // --size 64 awesomeapp_fa_1.png
+                        // application-x.vnd-awesome
+                        registrations.append(
+                                "        xdg-icon-resource install "
+                                        + "--context mimetypes --size ")
+                                .append(size)
+                                .append(" ")
+                                .append(LINUX_INSTALL_DIR.fetchFrom(params))
+                                .append("/")
+                                .append(data.get("APPLICATION_FS_NAME"))
+                                .append("/")
+                                .append(target.getName())
+                                .append(" ")
+                                .append(dashMime)
+                                .append("\n");
+
+                        // x dg-icon-resource uninstall --context mimetypes
+                        // --size 64 awesomeapp_fa_1.png
+                        // application-x.vnd-awesome
+                        deregistrations.append(
+                                "        xdg-icon-resource uninstall "
+                                        + "--context mimetypes --size ")
+                                .append(size)
+                                .append(" ")
+                                .append(LINUX_INSTALL_DIR.fetchFrom(params))
+                                .append("/")
+                                .append(data.get("APPLICATION_FS_NAME"))
+                                .append("/")
+                                .append(target.getName())
+                                .append(" ")
+                                .append(dashMime)
+                                .append("\n");
+                    }
+                }
+            }
+            mimeInfo.append("</mime-info>");
+
+            if (addedEntry) {
+                Writer w = new BufferedWriter(new FileWriter(
+                        new File(rootDir, mimeInfoFile)));
+                w.write(mimeInfo.toString());
+                w.close();
+                data.put("FILE_ASSOCIATION_INSTALL", registrations.toString());
+                data.put("FILE_ASSOCIATION_REMOVE", deregistrations.toString());
+                data.put("DESKTOP_MIMES", desktopMimes.toString());
+            }
+        }
+
+        if (!RUNTIME_INSTALLER.fetchFrom(params)) {
+            //prepare desktop shortcut
+            Writer w = new BufferedWriter(new FileWriter(
+                    getConfig_DesktopShortcutFile(rootDir, params)));
+            String content = preprocessTextResource(
+                    getConfig_DesktopShortcutFile(
+                    rootDir, params).getName(),
+                    I18N.getString("resource.menu-shortcut-descriptor"),
+                    DEFAULT_DESKTOP_FILE_TEMPLATE,
+                    data,
+                    VERBOSE.fetchFrom(params),
+                    RESOURCE_DIR.fetchFrom(params));
+            w.write(content);
+            w.close();
+        }
+        // prepare control file
+        Writer w = new BufferedWriter(new FileWriter(
+                getConfig_ControlFile(params)));
+        String content = preprocessTextResource(
+                getConfig_ControlFile(params).getName(),
+                I18N.getString("resource.deb-control-file"),
+                DEFAULT_CONTROL_TEMPLATE,
+                data,
+                VERBOSE.fetchFrom(params),
+                RESOURCE_DIR.fetchFrom(params));
+        w.write(content);
+        w.close();
+
+        w = new BufferedWriter(new FileWriter(
+                getConfig_PreinstallFile(params)));
+        content = preprocessTextResource(
+                getConfig_PreinstallFile(params).getName(),
+                I18N.getString("resource.deb-preinstall-script"),
+                DEFAULT_PREINSTALL_TEMPLATE,
+                data,
+                VERBOSE.fetchFrom(params),
+                RESOURCE_DIR.fetchFrom(params));
+        w.write(content);
+        w.close();
+        setPermissions(getConfig_PreinstallFile(params), "rwxr-xr-x");
+
+        w = new BufferedWriter(new FileWriter(getConfig_PrermFile(params)));
+        content = preprocessTextResource(
+                getConfig_PrermFile(params).getName(),
+                I18N.getString("resource.deb-prerm-script"),
+                DEFAULT_PRERM_TEMPLATE,
+                data,
+                VERBOSE.fetchFrom(params),
+                RESOURCE_DIR.fetchFrom(params));
+        w.write(content);
+        w.close();
+        setPermissions(getConfig_PrermFile(params), "rwxr-xr-x");
+
+        w = new BufferedWriter(new FileWriter(
+                getConfig_PostinstallFile(params)));
+        content = preprocessTextResource(
+                getConfig_PostinstallFile(params).getName(),
+                I18N.getString("resource.deb-postinstall-script"),
+                DEFAULT_POSTINSTALL_TEMPLATE,
+                data,
+                VERBOSE.fetchFrom(params),
+                RESOURCE_DIR.fetchFrom(params));
+        w.write(content);
+        w.close();
+        setPermissions(getConfig_PostinstallFile(params), "rwxr-xr-x");
+
+        w = new BufferedWriter(new FileWriter(getConfig_PostrmFile(params)));
+        content = preprocessTextResource(
+                getConfig_PostrmFile(params).getName(),
+                I18N.getString("resource.deb-postrm-script"),
+                DEFAULT_POSTRM_TEMPLATE,
+                data,
+                VERBOSE.fetchFrom(params),
+                RESOURCE_DIR.fetchFrom(params));
+        w.write(content);
+        w.close();
+        setPermissions(getConfig_PostrmFile(params), "rwxr-xr-x");
+
+        w = new BufferedWriter(new FileWriter(getConfig_CopyrightFile(params)));
+        content = preprocessTextResource(
+                getConfig_CopyrightFile(params).getName(),
+                I18N.getString("resource.deb-copyright-file"),
+                DEFAULT_COPYRIGHT_TEMPLATE,
+                data,
+                VERBOSE.fetchFrom(params),
+                RESOURCE_DIR.fetchFrom(params));
+        w.write(content);
+        w.close();
+
+        return true;
+    }
+
+    private Map<String, String> createReplacementData(
+            Map<String, ? super Object> params) {
+        Map<String, String> data = new HashMap<>();
+
+        data.put("APPLICATION_NAME", APP_NAME.fetchFrom(params));
+        data.put("APPLICATION_FS_NAME", APP_NAME.fetchFrom(params));
+        data.put("APPLICATION_PACKAGE", BUNDLE_NAME.fetchFrom(params));
+        data.put("APPLICATION_VENDOR", VENDOR.fetchFrom(params));
+        data.put("APPLICATION_MAINTAINER", MAINTAINER.fetchFrom(params));
+        data.put("APPLICATION_VERSION", VERSION.fetchFrom(params));
+        data.put("APPLICATION_LAUNCHER_FILENAME", APP_NAME.fetchFrom(params));
+        data.put("INSTALLATION_DIRECTORY", LINUX_INSTALL_DIR.fetchFrom(params));
+        data.put("XDG_PREFIX", XDG_FILE_PREFIX.fetchFrom(params));
+        data.put("DEPLOY_BUNDLE_CATEGORY", CATEGORY.fetchFrom(params));
+        data.put("APPLICATION_DESCRIPTION", DESCRIPTION.fetchFrom(params));
+        data.put("APPLICATION_SUMMARY", TITLE.fetchFrom(params));
+        data.put("APPLICATION_COPYRIGHT", COPYRIGHT.fetchFrom(params));
+        data.put("APPLICATION_LICENSE_TEXT", LICENSE_TEXT.fetchFrom(params));
+        data.put("APPLICATION_ARCH", getArch());
+        data.put("APPLICATION_INSTALLED_SIZE",
+                Long.toString(getInstalledSizeKB(params)));
+        String deps = LINUX_PACKAGE_DEPENDENCIES.fetchFrom(params);
+        data.put("PACKAGE_DEPENDENCIES",
+                deps.isEmpty() ? "" : "Depends: " + deps);
+        data.put("RUNTIME_INSTALLER",
+                RUNTIME_INSTALLER.fetchFrom(params).toString());
+
+        return data;
+    }
+
+    private File getConfig_DesktopShortcutFile(File rootDir,
+            Map<String, ? super Object> params) {
+        return new File(rootDir, APP_NAME.fetchFrom(params) + ".desktop");
+    }
+
+    private File getConfig_IconFile(File rootDir,
+            Map<String, ? super Object> params) {
+        return new File(rootDir, APP_NAME.fetchFrom(params) + ".png");
+    }
+
+    private File getConfig_InitScriptFile(Map<String, ? super Object> params) {
+        return new File(LinuxAppBundler.getRootDir(
+                APP_IMAGE_ROOT.fetchFrom(params), params),
+                        BUNDLE_NAME.fetchFrom(params) + ".init");
+    }
+
+    private File getConfig_ControlFile(Map<String, ? super Object> params) {
+        return new File(CONFIG_DIR.fetchFrom(params), "control");
+    }
+
+    private File getConfig_PreinstallFile(Map<String, ? super Object> params) {
+        return new File(CONFIG_DIR.fetchFrom(params), "preinst");
+    }
+
+    private File getConfig_PrermFile(Map<String, ? super Object> params) {
+        return new File(CONFIG_DIR.fetchFrom(params), "prerm");
+    }
+
+    private File getConfig_PostinstallFile(Map<String, ? super Object> params) {
+        return new File(CONFIG_DIR.fetchFrom(params), "postinst");
+    }
+
+    private File getConfig_PostrmFile(Map<String, ? super Object> params) {
+        return new File(CONFIG_DIR.fetchFrom(params), "postrm");
+    }
+
+    private File getConfig_CopyrightFile(Map<String, ? super Object> params) {
+        return new File(CONFIG_DIR.fetchFrom(params), "copyright");
+    }
+
+    private File buildDeb(Map<String, ? super Object> params,
+            File outdir) throws IOException {
+        File outFile = new File(outdir,
+                FULL_PACKAGE_NAME.fetchFrom(params)+".deb");
+        Log.verbose(MessageFormat.format(I18N.getString(
+                "message.outputting-to-location"), outFile.getAbsolutePath()));
+
+        outFile.getParentFile().mkdirs();
+
+        // run dpkg
+        ProcessBuilder pb = new ProcessBuilder(
+                "fakeroot", TOOL_DPKG, "-b",
+                FULL_PACKAGE_NAME.fetchFrom(params),
+                outFile.getAbsolutePath());
+        pb = pb.directory(DEB_IMAGE_DIR.fetchFrom(params).getParentFile());
+        IOUtils.exec(pb, false);
+
+        Log.verbose(MessageFormat.format(I18N.getString(
+                "message.output-to-location"), outFile.getAbsolutePath()));
+
+        return outFile;
+    }
+
+    @Override
+    public String getName() {
+        return I18N.getString("deb.bundler.name");
+    }
+
+    @Override
+    public String getDescription() {
+        return I18N.getString("deb.bundler.description");
+    }
+
+    @Override
+    public String getID() {
+        return "deb";
+    }
+
+    @Override
+    public String getBundleType() {
+        return "INSTALLER";
+    }
+
+    @Override
+    public Collection<BundlerParamInfo<?>> getBundleParameters() {
+        Collection<BundlerParamInfo<?>> results = new LinkedHashSet<>();
+        results.addAll(LinuxAppBundler.getAppBundleParameters());
+        results.addAll(getDebBundleParameters());
+        return results;
+    }
+
+    public static Collection<BundlerParamInfo<?>> getDebBundleParameters() {
+        return Arrays.asList(
+                BUNDLE_NAME,
+                COPYRIGHT,
+                CATEGORY,
+                DESCRIPTION,
+                EMAIL,
+                ICON_PNG,
+                LICENSE_FILE,
+                TITLE,
+                VENDOR
+        );
+    }
+
+    @Override
+    public File execute(Map<String, ? super Object> params,
+            File outputParentDir) throws PackagerException {
+        return bundle(params, outputParentDir);
+    }
+
+    @Override
+    public boolean supported(boolean runtimeInstaller) {
+        return (Platform.getPlatform() == Platform.LINUX);
+    }
+
+    public int getSquareSizeOfImage(File f) {
+        try {
+            BufferedImage bi = ImageIO.read(f);
+            if (bi.getWidth() == bi.getHeight()) {
+                return bi.getWidth();
+            } else {
+                return 0;
+            }
+        } catch (Exception e) {
+            Log.verbose(e);
+            return 0;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxRpmBundler.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,728 @@
+/*
+ * Copyright (c) 2012, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jpackage.internal;
+
+import javax.imageio.ImageIO;
+import java.awt.image.BufferedImage;
+import java.io.*;
+import java.nio.file.Files;
+import java.nio.file.attribute.PosixFilePermission;
+import java.nio.file.attribute.PosixFilePermissions;
+import java.text.MessageFormat;
+import java.util.*;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import static jdk.jpackage.internal.StandardBundlerParam.*;
+import static jdk.jpackage.internal.LinuxAppBundler.LINUX_INSTALL_DIR;
+import static jdk.jpackage.internal.LinuxAppBundler.LINUX_PACKAGE_DEPENDENCIES;
+
+public class LinuxRpmBundler extends AbstractBundler {
+
+    private static final ResourceBundle I18N = ResourceBundle.getBundle(
+            "jdk.jpackage.internal.resources.LinuxResources");
+
+    public static final BundlerParamInfo<LinuxAppBundler> APP_BUNDLER =
+            new StandardBundlerParam<>(
+            I18N.getString("param.rpm-app-bundler.name"),
+            I18N.getString("param.rpm-app-bundler.description"),
+            "linux.app.bundler",
+            LinuxAppBundler.class,
+            params -> new LinuxAppBundler(),
+            null);
+
+    public static final BundlerParamInfo<File> RPM_IMAGE_DIR =
+            new StandardBundlerParam<>(
+            I18N.getString("param.image-dir.name"),
+            I18N.getString("param.image-dir.description"),
+            "linux.rpm.imageDir",
+            File.class,
+            params -> {
+                File imagesRoot = IMAGES_ROOT.fetchFrom(params);
+                if (!imagesRoot.exists()) imagesRoot.mkdirs();
+                return new File(imagesRoot, "linux-rpm.image");
+            },
+            (s, p) -> new File(s));
+
+    // Fedora rules for package naming are used here
+    // https://fedoraproject.org/wiki/Packaging:NamingGuidelines?rd=Packaging/NamingGuidelines
+    //
+    // all Fedora packages must be named using only the following ASCII
+    // characters. These characters are displayed here:
+    //
+    // abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._+
+    //
+    private static final Pattern RPM_BUNDLE_NAME_PATTERN =
+            Pattern.compile("[a-z\\d\\+\\-\\.\\_]+", Pattern.CASE_INSENSITIVE);
+
+    public static final BundlerParamInfo<String> BUNDLE_NAME =
+            new StandardBundlerParam<> (
+            I18N.getString("param.bundle-name.name"),
+            I18N.getString("param.bundle-name.description"),
+            Arguments.CLIOptions.LINUX_BUNDLE_NAME.getId(),
+            String.class,
+            params -> {
+                String nm = APP_NAME.fetchFrom(params);
+                if (nm == null) return null;
+
+                // make sure to lower case and spaces become dashes
+                nm = nm.toLowerCase().replaceAll("[ ]", "-");
+
+                return nm;
+            },
+            (s, p) -> {
+                if (!RPM_BUNDLE_NAME_PATTERN.matcher(s).matches()) {
+                    String msgKey = "error.invalid-value-for-package-name";
+                    throw new IllegalArgumentException(
+                            new ConfigException(MessageFormat.format(
+                                    I18N.getString(msgKey), s),
+                                    I18N.getString(msgKey + ".advice")));
+                }
+
+                return s;
+            }
+        );
+
+    public static final BundlerParamInfo<String> LICENSE_TYPE =
+        new StandardBundlerParam<>(
+                I18N.getString("param.license-type.name"),
+                I18N.getString("param.license-type.description"),
+                Arguments.CLIOptions.LINUX_RPM_LICENSE_TYPE.getId(),
+                String.class,
+                params -> I18N.getString("param.license-type.default"),
+                (s, p) -> s
+        );
+
+    public static final BundlerParamInfo<String> XDG_FILE_PREFIX =
+            new StandardBundlerParam<> (
+            I18N.getString("param.xdg-prefix.name"),
+            I18N.getString("param.xdg-prefix.description"),
+            "linux.xdg-prefix",
+            String.class,
+            params -> {
+                try {
+                    String vendor;
+                    if (params.containsKey(VENDOR.getID())) {
+                        vendor = VENDOR.fetchFrom(params);
+                    } else {
+                        vendor = "jpackage";
+                    }
+                    String appName = APP_NAME.fetchFrom(params);
+
+                    return (vendor + "-" + appName).replaceAll("\\s", "");
+                } catch (Exception e) {
+                    if (Log.isDebug()) {
+                        e.printStackTrace();
+                    }
+                }
+                return "unknown-MimeInfo.xml";
+            },
+            (s, p) -> s);
+
+    private final static String DEFAULT_ICON = "javalogo_white_32.png";
+    private final static String DEFAULT_SPEC_TEMPLATE = "template.spec";
+    private final static String DEFAULT_DESKTOP_FILE_TEMPLATE =
+            "template.desktop";
+
+    public final static String TOOL_RPMBUILD = "rpmbuild";
+    public final static double TOOL_RPMBUILD_MIN_VERSION = 4.0d;
+
+    public static boolean testTool(String toolName, double minVersion) {
+        try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
+                PrintStream ps = new PrintStream(baos)) {
+            ProcessBuilder pb = new ProcessBuilder(toolName, "--version");
+            IOUtils.exec(pb, Log.isDebug(), false, ps);
+                    //not interested in the above's output
+            String content = new String(baos.toByteArray());
+            Pattern pattern = Pattern.compile(" (\\d+\\.\\d+)");
+            Matcher matcher = pattern.matcher(content);
+
+            if (matcher.find()) {
+                String v = matcher.group(1);
+                double version = Double.parseDouble(v);
+                return minVersion <= version;
+            } else {
+               return false;
+            }
+        } catch (Exception e) {
+            Log.verbose(MessageFormat.format(I18N.getString(
+                    "message.test-for-tool"), toolName, e.getMessage()));
+            return false;
+        }
+    }
+
+    @Override
+    public boolean validate(Map<String, ? super Object> p)
+            throws UnsupportedPlatformException, ConfigException {
+        try {
+            if (p == null) throw new ConfigException(
+                    I18N.getString("error.parameters-null"),
+                    I18N.getString("error.parameters-null.advice"));
+
+            // run basic validation to ensure requirements are met
+            // we are not interested in return code, only possible exception
+            APP_BUNDLER.fetchFrom(p).validate(p);
+
+            // validate presense of required tools
+            if (!testTool(TOOL_RPMBUILD, TOOL_RPMBUILD_MIN_VERSION)){
+                throw new ConfigException(
+                    MessageFormat.format(
+                        I18N.getString("error.cannot-find-rpmbuild"),
+                        TOOL_RPMBUILD_MIN_VERSION),
+                    MessageFormat.format(
+                        I18N.getString("error.cannot-find-rpmbuild.advice"),
+                        TOOL_RPMBUILD_MIN_VERSION));
+            }
+
+            // only one mime type per association, at least one file extension
+            List<Map<String, ? super Object>> associations =
+                    FILE_ASSOCIATIONS.fetchFrom(p);
+            if (associations != null) {
+                for (int i = 0; i < associations.size(); i++) {
+                    Map<String, ? super Object> assoc = associations.get(i);
+                    List<String> mimes = FA_CONTENT_TYPE.fetchFrom(assoc);
+                    if (mimes == null || mimes.isEmpty()) {
+                        String msgKey =
+                                "error.no-content-types-for-file-association";
+                        throw new ConfigException(
+                                MessageFormat.format(I18N.getString(msgKey), i),
+                                I18N.getString(msgKey + ".advice"));
+                    } else if (mimes.size() > 1) {
+                        String msgKey =
+                                "error.no-content-types-for-file-association";
+                        throw new ConfigException(
+                                MessageFormat.format(I18N.getString(msgKey), i),
+                                I18N.getString(msgKey + ".advice"));
+                    }
+                }
+            }
+
+            // bundle name has some restrictions
+            // the string converter will throw an exception if invalid
+            BUNDLE_NAME.getStringConverter().apply(BUNDLE_NAME.fetchFrom(p), p);
+
+            return true;
+        } catch (RuntimeException re) {
+            if (re.getCause() instanceof ConfigException) {
+                throw (ConfigException) re.getCause();
+            } else {
+                throw new ConfigException(re);
+            }
+        }
+    }
+
+    private boolean prepareProto(Map<String, ? super Object> p)
+            throws PackagerException, IOException {
+        File appImage = StandardBundlerParam.getPredefinedAppImage(p);
+        File appDir = null;
+
+        // we either have an application image or need to build one
+        if (appImage != null) {
+            appDir = new File(RPM_IMAGE_DIR.fetchFrom(p),
+                APP_NAME.fetchFrom(p));
+            // copy everything from appImage dir into appDir/name
+            IOUtils.copyRecursive(appImage.toPath(), appDir.toPath());
+        } else {
+            appDir = APP_BUNDLER.fetchFrom(p).doBundle(p,
+                    RPM_IMAGE_DIR.fetchFrom(p), true);
+        }
+        return appDir != null;
+    }
+
+    public File bundle(Map<String, ? super Object> p,
+            File outdir) throws PackagerException {
+        if (!outdir.isDirectory() && !outdir.mkdirs()) {
+            throw new PackagerException(
+                    "error.cannot-create-output-dir",
+                    outdir.getAbsolutePath());
+        }
+        if (!outdir.canWrite()) {
+            throw new PackagerException(
+                    "error.cannot-write-to-output-dir",
+                    outdir.getAbsolutePath());
+        }
+
+        File imageDir = RPM_IMAGE_DIR.fetchFrom(p);
+        try {
+
+            imageDir.mkdirs();
+
+            if (prepareProto(p) && prepareProjectConfig(p)) {
+                return buildRPM(p, outdir);
+            }
+            return null;
+        } catch (IOException ex) {
+            Log.verbose(ex);
+            throw new PackagerException(ex);
+        }
+    }
+
+    private String getLicenseFileString(Map<String, ? super Object> params)
+            throws IOException {
+        StringBuilder sb = new StringBuilder();
+
+        String licenseStr = LICENSE_FILE.fetchFrom(params);
+        if (licenseStr != null) {
+            File licenseFile = new File(licenseStr);
+            File rootDir =
+                    LinuxAppBundler.getRootDir(RPM_IMAGE_DIR.fetchFrom(params),
+                            params);
+            File target = new File(rootDir + File.separator + "app"
+                    + File.separator + licenseFile.getName());
+            Files.copy(licenseFile.toPath(), target.toPath());
+
+            sb.append("%license ");
+            sb.append(LINUX_INSTALL_DIR.fetchFrom(params));
+            sb.append("/");
+            sb.append(APP_NAME.fetchFrom(params));
+            sb.append("/app/");
+            sb.append(licenseFile.getName());
+        }
+
+        return sb.toString();
+    }
+
+    private boolean prepareProjectConfig(Map<String, ? super Object> params)
+            throws IOException {
+        Map<String, String> data = createReplacementData(params);
+        File rootDir =
+            LinuxAppBundler.getRootDir(RPM_IMAGE_DIR.fetchFrom(params), params);
+
+        // prepare installer icon
+        File iconTarget = getConfig_IconFile(rootDir, params);
+        File icon = LinuxAppBundler.ICON_PNG.fetchFrom(params);
+        if (!RUNTIME_INSTALLER.fetchFrom(params)) {
+            if (icon == null || !icon.exists()) {
+                fetchResource(iconTarget.getName(),
+                        I18N.getString("resource.menu-icon"),
+                        DEFAULT_ICON,
+                        iconTarget,
+                        VERBOSE.fetchFrom(params),
+                        RESOURCE_DIR.fetchFrom(params));
+            } else {
+                fetchResource(iconTarget.getName(),
+                        I18N.getString("resource.menu-icon"),
+                        icon,
+                        iconTarget,
+                        VERBOSE.fetchFrom(params),
+                        RESOURCE_DIR.fetchFrom(params));
+            }
+        }
+
+        StringBuilder installScripts = new StringBuilder();
+        StringBuilder removeScripts = new StringBuilder();
+        for (Map<String, ? super Object> secondaryLauncher :
+                SECONDARY_LAUNCHERS.fetchFrom(params)) {
+            Map<String, String> secondaryLauncherData =
+                    createReplacementData(secondaryLauncher);
+            secondaryLauncherData.put("APPLICATION_FS_NAME",
+                    data.get("APPLICATION_FS_NAME"));
+            secondaryLauncherData.put("DESKTOP_MIMES", "");
+
+            // prepare desktop shortcut
+            Writer w = new BufferedWriter(new FileWriter(
+                    getConfig_DesktopShortcutFile(rootDir, secondaryLauncher)));
+            String content = preprocessTextResource(
+                    getConfig_DesktopShortcutFile(rootDir,
+                    secondaryLauncher).getName(),
+                    I18N.getString("resource.menu-shortcut-descriptor"),
+                    DEFAULT_DESKTOP_FILE_TEMPLATE, secondaryLauncherData,
+                    VERBOSE.fetchFrom(params),
+                    RESOURCE_DIR.fetchFrom(params));
+            w.write(content);
+            w.close();
+
+            // prepare installer icon
+            iconTarget = getConfig_IconFile(rootDir, secondaryLauncher);
+            icon = LinuxAppBundler.ICON_PNG.fetchFrom(secondaryLauncher);
+            if (icon == null || !icon.exists()) {
+                fetchResource(iconTarget.getName(),
+                        I18N.getString("resource.menu-icon"),
+                        DEFAULT_ICON,
+                        iconTarget,
+                        VERBOSE.fetchFrom(params),
+                        RESOURCE_DIR.fetchFrom(params));
+            } else {
+                fetchResource(iconTarget.getName(),
+                        I18N.getString("resource.menu-icon"),
+                        icon,
+                        iconTarget,
+                        VERBOSE.fetchFrom(params),
+                        RESOURCE_DIR.fetchFrom(params));
+            }
+
+            // post copying of desktop icon
+            installScripts.append("xdg-desktop-menu install --novendor ");
+            installScripts.append(LINUX_INSTALL_DIR.fetchFrom(params));
+            installScripts.append("/");
+            installScripts.append(data.get("APPLICATION_FS_NAME"));
+            installScripts.append("/");
+            installScripts.append(secondaryLauncherData.get(
+                    "APPLICATION_LAUNCHER_FILENAME"));
+            installScripts.append(".desktop\n");
+
+            // preun cleanup of desktop icon
+            removeScripts.append("xdg-desktop-menu uninstall --novendor ");
+            removeScripts.append(LINUX_INSTALL_DIR.fetchFrom(params));
+            removeScripts.append("/");
+            removeScripts.append(data.get("APPLICATION_FS_NAME"));
+            removeScripts.append("/");
+            removeScripts.append(secondaryLauncherData.get(
+                    "APPLICATION_LAUNCHER_FILENAME"));
+            removeScripts.append(".desktop\n");
+
+        }
+        data.put("SECONDARY_LAUNCHERS_INSTALL", installScripts.toString());
+        data.put("SECONDARY_LAUNCHERS_REMOVE", removeScripts.toString());
+
+        StringBuilder cdsScript = new StringBuilder();
+
+        data.put("APP_CDS_CACHE", cdsScript.toString());
+
+        List<Map<String, ? super Object>> associations =
+                FILE_ASSOCIATIONS.fetchFrom(params);
+        data.put("FILE_ASSOCIATION_INSTALL", "");
+        data.put("FILE_ASSOCIATION_REMOVE", "");
+        data.put("DESKTOP_MIMES", "");
+        if (associations != null) {
+            String mimeInfoFile = XDG_FILE_PREFIX.fetchFrom(params)
+                    + "-MimeInfo.xml";
+            StringBuilder mimeInfo = new StringBuilder(
+                "<?xml version=\"1.0\"?>\n<mime-info xmlns="
+                +"'http://www.freedesktop.org/standards/shared-mime-info'>\n");
+            StringBuilder registrations = new StringBuilder();
+            StringBuilder deregistrations = new StringBuilder();
+            StringBuilder desktopMimes = new StringBuilder("MimeType=");
+            boolean addedEntry = false;
+
+            for (Map<String, ? super Object> assoc : associations) {
+                //  <mime-type type="application/x-vnd.awesome">
+                //    <comment>Awesome document</comment>
+                //    <glob pattern="*.awesome"/>
+                //    <glob pattern="*.awe"/>
+                //  </mime-type>
+
+                if (assoc == null) {
+                    continue;
+                }
+
+                String description = FA_DESCRIPTION.fetchFrom(assoc);
+                File faIcon = FA_ICON.fetchFrom(assoc); //TODO FA_ICON_PNG
+                List<String> extensions = FA_EXTENSIONS.fetchFrom(assoc);
+                if (extensions == null) {
+                    Log.verbose(I18N.getString(
+                        "message.creating-association-with-null-extension"));
+                }
+
+                List<String> mimes = FA_CONTENT_TYPE.fetchFrom(assoc);
+                if (mimes == null || mimes.isEmpty()) {
+                    continue;
+                }
+                String thisMime = mimes.get(0);
+                String dashMime = thisMime.replace('/', '-');
+
+                mimeInfo.append("  <mime-type type='")
+                        .append(thisMime)
+                        .append("'>\n");
+                if (description != null && !description.isEmpty()) {
+                    mimeInfo.append("    <comment>")
+                            .append(description)
+                            .append("</comment>\n");
+                }
+
+                if (extensions != null) {
+                    for (String ext : extensions) {
+                        mimeInfo.append("    <glob pattern='*.")
+                                .append(ext)
+                                .append("'/>\n");
+                    }
+                }
+
+                mimeInfo.append("  </mime-type>\n");
+                if (!addedEntry) {
+                    registrations.append("xdg-mime install ")
+                            .append(LINUX_INSTALL_DIR.fetchFrom(params))
+                            .append("/")
+                            .append(data.get("APPLICATION_FS_NAME"))
+                            .append("/")
+                            .append(mimeInfoFile)
+                            .append("\n");
+
+                    deregistrations.append("xdg-mime uninstall ")
+                            .append(LINUX_INSTALL_DIR.fetchFrom(params))
+                            .append("/")
+                            .append(data.get("APPLICATION_FS_NAME"))
+                            .append("/")
+                            .append(mimeInfoFile)
+                            .append("\n");
+                    addedEntry = true;
+                } else {
+                    desktopMimes.append(";");
+                }
+                desktopMimes.append(thisMime);
+
+                if (faIcon != null && faIcon.exists()) {
+                    int size = getSquareSizeOfImage(faIcon);
+
+                    if (size > 0) {
+                        File target = new File(rootDir,
+                                APP_NAME.fetchFrom(params)
+                                + "_fa_" + faIcon.getName());
+                        IOUtils.copyFile(faIcon, target);
+
+                        // xdg-icon-resource install --context mimetypes
+                        // --size 64 awesomeapp_fa_1.png
+                        // application-x.vnd-awesome
+                        registrations.append(
+                                "xdg-icon-resource install "
+                                + "--context mimetypes --size ")
+                                .append(size)
+                                .append(" ")
+                                .append(LINUX_INSTALL_DIR.fetchFrom(params))
+                                .append("/")
+                                .append(data.get("APPLICATION_FS_NAME"))
+                                .append("/")
+                                .append(target.getName())
+                                .append(" ")
+                                .append(dashMime)
+                                .append("\n");
+
+                        // xdg-icon-resource uninstall --context mimetypes
+                        // --size 64 awesomeapp_fa_1.png
+                        // application-x.vnd-awesome
+                        deregistrations.append(
+                                "xdg-icon-resource uninstall "
+                                + "--context mimetypes --size ")
+                                .append(size)
+                                .append(" ")
+                                .append(LINUX_INSTALL_DIR.fetchFrom(params))
+                                .append("/")
+                                .append(data.get("APPLICATION_FS_NAME"))
+                                .append("/")
+                                .append(target.getName())
+                                .append(" ")
+                                .append(dashMime)
+                                .append("\n");
+                    }
+                }
+            }
+            mimeInfo.append("</mime-info>");
+
+            if (addedEntry) {
+                Writer w = new BufferedWriter(new FileWriter(
+                        new File(rootDir, mimeInfoFile)));
+                w.write(mimeInfo.toString());
+                w.close();
+                data.put("FILE_ASSOCIATION_INSTALL", registrations.toString());
+                data.put("FILE_ASSOCIATION_REMOVE", deregistrations.toString());
+                data.put("DESKTOP_MIMES", desktopMimes.toString());
+            }
+        }
+
+        if (!RUNTIME_INSTALLER.fetchFrom(params)) {
+            //prepare desktop shortcut
+            Writer w = new BufferedWriter(new FileWriter(
+                    getConfig_DesktopShortcutFile(rootDir, params)));
+            String content = preprocessTextResource(
+                    getConfig_DesktopShortcutFile(rootDir, params).getName(),
+                    I18N.getString("resource.menu-shortcut-descriptor"),
+                    DEFAULT_DESKTOP_FILE_TEMPLATE, data,
+                    VERBOSE.fetchFrom(params),
+                    RESOURCE_DIR.fetchFrom(params));
+            w.write(content);
+            w.close();
+        }
+
+        // prepare spec file
+        Writer w = new BufferedWriter(
+                new FileWriter(getConfig_SpecFile(params)));
+        String content = preprocessTextResource(
+                getConfig_SpecFile(params).getName(),
+                I18N.getString("resource.rpm-spec-file"),
+                DEFAULT_SPEC_TEMPLATE, data,
+                VERBOSE.fetchFrom(params),
+                RESOURCE_DIR.fetchFrom(params));
+        w.write(content);
+        w.close();
+
+        return true;
+    }
+
+    private Map<String, String> createReplacementData(
+            Map<String, ? super Object> params) throws IOException {
+        Map<String, String> data = new HashMap<>();
+
+        data.put("APPLICATION_NAME", APP_NAME.fetchFrom(params));
+        data.put("APPLICATION_FS_NAME", APP_NAME.fetchFrom(params));
+        data.put("APPLICATION_PACKAGE", BUNDLE_NAME.fetchFrom(params));
+        data.put("APPLICATION_VENDOR", VENDOR.fetchFrom(params));
+        data.put("APPLICATION_VERSION", VERSION.fetchFrom(params));
+        data.put("APPLICATION_LAUNCHER_FILENAME", APP_NAME.fetchFrom(params));
+        data.put("INSTALLATION_DIRECTORY", LINUX_INSTALL_DIR.fetchFrom(params));
+        data.put("XDG_PREFIX", XDG_FILE_PREFIX.fetchFrom(params));
+        data.put("DEPLOY_BUNDLE_CATEGORY", CATEGORY.fetchFrom(params));
+        // TODO rpm categories
+        data.put("APPLICATION_DESCRIPTION", DESCRIPTION.fetchFrom(params));
+        data.put("APPLICATION_SUMMARY", TITLE.fetchFrom(params));
+        data.put("APPLICATION_LICENSE_TYPE", LICENSE_TYPE.fetchFrom(params));
+        data.put("APPLICATION_LICENSE_FILE", getLicenseFileString(params));
+        String deps = LINUX_PACKAGE_DEPENDENCIES.fetchFrom(params);
+        data.put("PACKAGE_DEPENDENCIES",
+                deps.isEmpty() ? "" : "Requires: " + deps);
+        data.put("RUNTIME_INSTALLER",
+                RUNTIME_INSTALLER.fetchFrom(params).toString());
+        return data;
+    }
+
+    private File getConfig_DesktopShortcutFile(File rootDir,
+            Map<String, ? super Object> params) {
+        return new File(rootDir, APP_NAME.fetchFrom(params) + ".desktop");
+    }
+
+    private File getConfig_IconFile(File rootDir,
+            Map<String, ? super Object> params) {
+        return new File(rootDir, APP_NAME.fetchFrom(params) + ".png");
+    }
+
+    private File getConfig_SpecFile(Map<String, ? super Object> params) {
+        return new File(RPM_IMAGE_DIR.fetchFrom(params),
+                APP_NAME.fetchFrom(params) + ".spec");
+    }
+
+    private File buildRPM(Map<String, ? super Object> params,
+            File outdir) throws IOException {
+        Log.verbose(MessageFormat.format(I18N.getString(
+                "message.outputting-bundle-location"),
+                outdir.getAbsolutePath()));
+
+        File broot = new File(BUILD_ROOT.fetchFrom(params), "rmpbuildroot");
+
+        outdir.mkdirs();
+
+        //run rpmbuild
+        ProcessBuilder pb = new ProcessBuilder(
+                TOOL_RPMBUILD,
+                "-bb", getConfig_SpecFile(params).getAbsolutePath(),
+                "--define", "%_sourcedir "
+                        + RPM_IMAGE_DIR.fetchFrom(params).getAbsolutePath(),
+                // save result to output dir
+                "--define", "%_rpmdir " + outdir.getAbsolutePath(),
+                // do not use other system directories to build as current user
+                "--define", "%_topdir " + broot.getAbsolutePath()
+        );
+        pb = pb.directory(RPM_IMAGE_DIR.fetchFrom(params));
+        IOUtils.exec(pb, false);
+
+        Log.verbose(MessageFormat.format(
+                I18N.getString("message.output-bundle-location"),
+                outdir.getAbsolutePath()));
+
+        // presume the result is the ".rpm" file with the newest modified time
+        // not the best solution, but it is the most reliable
+        File result = null;
+        long lastModified = 0;
+        File[] list = outdir.listFiles();
+        if (list != null) {
+            for (File f : list) {
+                if (f.getName().endsWith(".rpm") &&
+                        f.lastModified() > lastModified) {
+                    result = f;
+                    lastModified = f.lastModified();
+                }
+            }
+        }
+
+        return result;
+    }
+
+    @Override
+    public String getName() {
+        return I18N.getString("rpm.bundler.name");
+    }
+
+    @Override
+    public String getDescription() {
+        return I18N.getString("rpm.bundler.description");
+    }
+
+    @Override
+    public String getID() {
+        return "rpm";
+    }
+
+    @Override
+    public String getBundleType() {
+        return "INSTALLER";
+    }
+
+    @Override
+    public Collection<BundlerParamInfo<?>> getBundleParameters() {
+        Collection<BundlerParamInfo<?>> results = new LinkedHashSet<>();
+        results.addAll(LinuxAppBundler.getAppBundleParameters());
+        results.addAll(getRpmBundleParameters());
+        return results;
+    }
+
+    public static Collection<BundlerParamInfo<?>> getRpmBundleParameters() {
+        return Arrays.asList(
+                BUNDLE_NAME,
+                CATEGORY,
+                DESCRIPTION,
+                LinuxAppBundler.ICON_PNG,
+                LICENSE_FILE,
+                LICENSE_TYPE,
+                TITLE,
+                VENDOR
+        );
+    }
+
+    @Override
+    public File execute(Map<String, ? super Object> params,
+            File outputParentDir) throws PackagerException {
+        return bundle(params, outputParentDir);
+    }
+
+    @Override
+    public boolean supported(boolean runtimeInstaller) {
+        return (Platform.getPlatform() == Platform.LINUX);
+    }
+
+    public int getSquareSizeOfImage(File f) {
+        try {
+            BufferedImage bi = ImageIO.read(f);
+            if (bi.getWidth() == bi.getHeight()) {
+                return bi.getWidth();
+            } else {
+                return 0;
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+            return 0;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources.properties	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,109 @@
+#
+# Copyright (c) 2017, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+# particular file as subject to the "Classpath" exception as provided
+# by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+# or visit www.oracle.com if you need additional information or have any
+# questions.
+#
+#
+
+app.bundler.name=Linux Application Image
+app.bundler.description=A Directory based image of a linux Application with an optionally co-bundled JRE.  Used as a base for the Installer bundlers.
+deb.bundler.name=DEB Installer
+deb.bundler.description=Linux Debian Bundle.
+rpm.bundler.name=RPM Bundle
+rpm.bundler.description=Redhat Package Manager (RPM) bundler.
+
+param.icon-png.name=.png Icon
+param.icon-png.description=Icon for the application, in PNG format.
+param.linux-install-dir.name=Linux Installation Directory
+param.linux-install-dir.description=Installation directory of the application on Linux.
+param.linux-package-dependencies.name=Linux Package Dependencies
+param.linux-package-dependencies.description=Required packages or capabilities for the application.
+param.deb-app-bundler.name=DEB Bundler Name
+param.deb-app-bundler.description=DEB Bundler Name
+param.full-package-name.name=Deb Package Name
+param.full-package-name.description=Deb Package Name
+param.image-dir.name=Image Dir
+param.image-dir.description=Image Dir
+param.app-image-root.name=Image Root Dir
+param.app-image-root.description=Image Root Dir
+param.config-dir.name=Config Dir
+param.config-dir.description=Config Dir
+param.maintainer-email.name=DEB Maintainer Email
+param.maintainer-email.description=DEB Maintainer Email
+param.maintainer-name.name=DEB Maintainer Name
+param.maintainer-name.description=DEB Maintainer Name
+param.license-type.name=License Type
+param.license-type.description=License Type
+param.license-type.default=Unknown
+param.license-text.name=License Content
+param.license-text.description=License Content
+param.xdg-prefix.name=Prefix for XDG files (mime, desktop)
+param.xdg-prefix.description=Prefix for XDG MimeInfo and Desktop Files.  Defaults to <vendor>-<appName>, with spaces dropped.
+param.rpm-app-bundler.name=RPM Bundler
+param.rpm-app-bundler.description=RPM Bundler
+param.bundle-name.name=RPM Bundle
+param.bundle-name.description=RPM Bundle
+
+resource.deb-control-file=DEB control file
+resource.deb-preinstall-script=DEB preinstall script
+resource.deb-prerm-script=DEB prerm script
+resource.deb-postinstall-script=DEB postinstall script
+resource.deb-postrm-script=DEB postrm script
+resource.deb-copyright-file=DEB copyright file
+resource.deb-init-script=DEB init script
+resource.menu-shortcut-descriptor=Menu shortcut descriptor
+resource.menu-icon=menu icon
+resource.rpm-spec-file=RPM spec file
+resource.rpm-init-script=RPM init script
+
+
+error.parameters-null=Parameters map is null.
+error.parameters-null.advice=Pass in a non-null parameters map.
+error.parameters-null=Parameters map is null.
+error.parameters-null.advice=Pass in a non-null parameters map.
+error.tool-not-found=Can not find {0}.
+error.tool-not-found.advice=Please install required packages.
+error.launcher-name-too-long=The bundle name "{0}" is too long for a daemon.
+error.launcher-name-too-long.advice=Set a bundler argument "{0}" to a bundle name that is shorter than 16 characters.
+error.cannot-create-output-dir=Output directory {0} cannot be created.
+error.cannot-write-to-output-dir=Output directory {0} is not writable.
+error.no-content-types-for-file-association=No MIME types were specified for File Association number {0}.
+error.no-content-types-for-file-association.advice=For Linux Bundling specify one and only one MIME type for each file association.
+error.too-many-content-types-for-file-association=More than one MIME types was specified for File Association number {0}.
+error.too-many-content-types-for-file-association.advice=For Linux Bundling specify one and only one MIME type for each file association.
+error.no-support-for-peruser-daemons=Bundler doesn't support per-user daemons.
+error.no-support-for-peruser-daemons.advice=Make sure that the system wide hint is set to true.
+error.invalid-value-for-package-name=Invalid value "{0}" for the package name.
+error.invalid-value-for-package-name.advice=Set the "linux.bundleName" parameter to a valid Debian package name. Note that the package names must consist only of lower case letters (a-z), digits (0-9), plus (+) and minus (-) signs, and periods (.). They must be at least two characters long and must start with an alphanumeric character.
+error.cannot-find-rpmbuild=Can not find rpmbuild {0} or newer.
+error.cannot-find-rpmbuild.advice=\  Install packages needed to build RPM, version {0} or newer.
+
+
+message.icon-not-png=The specified icon "{0}" is not a PNG file and will not be used.  The default icon will be used in it's place.
+message.test-for-tool=Test for [{0}]. Result\: {1}
+message.outputting-to-location=Generating DEB for installer to\: {0}
+message.output-to-location=Package (.deb) saved to\: {0}
+message.debs-like-licenses=Debian packages should specify a license.  The absence of a license will cause some linux distributions to complain about the quality of the application.
+message.one-shortcut-required=At least one type of shortcut is required. Enabling menu shortcut.
+message.outputting-bundle-location=Generating RPM for installer to\: {0}
+message.output-bundle-location=Package (.rpm) saved to\: {0}
+message.creating-association-with-null-extension=Creating association with null extension.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources_ja.properties	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,109 @@
+#
+# Copyright (c) 2017, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+# particular file as subject to the "Classpath" exception as provided
+# by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+# or visit www.oracle.com if you need additional information or have any
+# questions.
+#
+#
+
+app.bundler.name=Linux Application Image
+app.bundler.description=A Directory based image of a linux Application with an optionally co-bundled JRE.  Used as a base for the Installer bundlers.
+deb.bundler.name=DEB Installer
+deb.bundler.description=Linux Debian Bundle.
+rpm.bundler.name=RPM Bundle
+rpm.bundler.description=Redhat Package Manager (RPM) bundler.
+
+param.icon-png.name=.png Icon
+param.icon-png.description=Icon for the application, in PNG format.
+param.linux-install-dir.name=Linux Installation Directory
+param.linux-install-dir.description=Installation directory of the application on Linux.
+param.linux-package-dependencies.name=Linux Package Dependencies
+param.linux-package-dependencies.description=Required packages or capabilities for the application.
+param.deb-app-bundler.name=DEB Bundler Name
+param.deb-app-bundler.description=DEB Bundler Name
+param.full-package-name.name=Deb Package Name
+param.full-package-name.description=Deb Package Name
+param.image-dir.name=Image Dir
+param.image-dir.description=Image Dir
+param.app-image-root.name=Image Root Dir
+param.app-image-root.description=Image Root Dir
+param.config-dir.name=Config Dir
+param.config-dir.description=Config Dir
+param.maintainer-email.name=DEB Maintainer Email
+param.maintainer-email.description=DEB Maintainer Email
+param.maintainer-name.name=DEB Maintainer Name
+param.maintainer-name.description=DEB Maintainer Name
+param.license-type.name=License Type
+param.license-type.description=License Type
+param.license-type.default=Unknown
+param.license-text.name=License Content
+param.license-text.description=License Content
+param.xdg-prefix.name=Prefix for XDG files (mime, desktop)
+param.xdg-prefix.description=Prefix for XDG MimeInfo and Desktop Files.  Defaults to <vendor>-<appName>, with spaces dropped.
+param.rpm-app-bundler.name=RPM Bundler
+param.rpm-app-bundler.description=RPM Bundler
+param.bundle-name.name=RPM Bundle
+param.bundle-name.description=RPM Bundle
+
+resource.deb-control-file=DEB control file
+resource.deb-preinstall-script=DEB preinstall script
+resource.deb-prerm-script=DEB prerm script
+resource.deb-postinstall-script=DEB postinstall script
+resource.deb-postrm-script=DEB postrm script
+resource.deb-copyright-file=DEB copyright file
+resource.deb-init-script=DEB init script
+resource.menu-shortcut-descriptor=Menu shortcut descriptor
+resource.menu-icon=menu icon
+resource.rpm-spec-file=RPM spec file
+resource.rpm-init-script=RPM init script
+
+
+error.parameters-null=Parameters map is null.
+error.parameters-null.advice=Pass in a non-null parameters map.
+error.parameters-null=Parameters map is null.
+error.parameters-null.advice=Pass in a non-null parameters map.
+error.tool-not-found=Can not find {0}.
+error.tool-not-found.advice=Please install required packages.
+error.launcher-name-too-long=The bundle name "{0}" is too long for a daemon.
+error.launcher-name-too-long.advice=Set a bundler argument "{0}" to a bundle name that is shorter than 16 characters.
+error.cannot-create-output-dir=Output directory {0} cannot be created.
+error.cannot-write-to-output-dir=Output directory {0} is not writable.
+error.no-content-types-for-file-association=No MIME types were specified for File Association number {0}.
+error.no-content-types-for-file-association.advice=For Linux Bundling specify one and only one MIME type for each file association.
+error.too-many-content-types-for-file-association=More than one MIME types was specified for File Association number {0}.
+error.too-many-content-types-for-file-association.advice=For Linux Bundling specify one and only one MIME type for each file association.
+error.no-support-for-peruser-daemons=Bundler doesn't support per-user daemons.
+error.no-support-for-peruser-daemons.advice=Make sure that the system wide hint is set to true.
+error.invalid-value-for-package-name=Invalid value "{0}" for the package name.
+error.invalid-value-for-package-name.advice=Set the "linux.bundleName" parameter to a valid Debian package name. Note that the package names must consist only of lower case letters (a-z), digits (0-9), plus (+) and minus (-) signs, and periods (.). They must be at least two characters long and must start with an alphanumeric character.
+error.cannot-find-rpmbuild=Can not find rpmbuild {0} or newer.
+error.cannot-find-rpmbuild.advice=\  Install packages needed to build RPM, version {0} or newer.
+
+
+message.icon-not-png=The specified icon "{0}" is not a PNG file and will not be used.  The default icon will be used in it's place.
+message.test-for-tool=Test for [{0}]. Result\: {1}
+message.outputting-to-location=Generating DEB for installer to\: {0}
+message.output-to-location=Package (.deb) saved to\: {0}
+message.debs-like-licenses=Debian packages should specify a license.  The absence of a license will cause some linux distributions to complain about the quality of the application.
+message.one-shortcut-required=At least one type of shortcut is required. Enabling menu shortcut.
+message.outputting-bundle-location=Generating RPM for installer to\: {0}
+message.output-bundle-location=Package (.rpm) saved to\: {0}
+message.creating-association-with-null-extension=Creating association with null extension.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources_zh_CN.properties	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,109 @@
+#
+# Copyright (c) 2017, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+# particular file as subject to the "Classpath" exception as provided
+# by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+# or visit www.oracle.com if you need additional information or have any
+# questions.
+#
+#
+
+app.bundler.name=Linux Application Image
+app.bundler.description=A Directory based image of a linux Application with an optionally co-bundled JRE.  Used as a base for the Installer bundlers.
+deb.bundler.name=DEB Installer
+deb.bundler.description=Linux Debian Bundle.
+rpm.bundler.name=RPM Bundle
+rpm.bundler.description=Redhat Package Manager (RPM) bundler.
+
+param.icon-png.name=.png Icon
+param.icon-png.description=Icon for the application, in PNG format.
+param.linux-install-dir.name=Linux Installation Directory
+param.linux-install-dir.description=Installation directory of the application on Linux.
+param.linux-package-dependencies.name=Linux Package Dependencies
+param.linux-package-dependencies.description=Required packages or capabilities for the application.
+param.deb-app-bundler.name=DEB Bundler Name
+param.deb-app-bundler.description=DEB Bundler Name
+param.full-package-name.name=Deb Package Name
+param.full-package-name.description=Deb Package Name
+param.image-dir.name=Image Dir
+param.image-dir.description=Image Dir
+param.app-image-root.name=Image Root Dir
+param.app-image-root.description=Image Root Dir
+param.config-dir.name=Config Dir
+param.config-dir.description=Config Dir
+param.maintainer-email.name=DEB Maintainer Email
+param.maintainer-email.description=DEB Maintainer Email
+param.maintainer-name.name=DEB Maintainer Name
+param.maintainer-name.description=DEB Maintainer Name
+param.license-type.name=License Type
+param.license-type.description=License Type
+param.license-type.default=Unknown
+param.license-text.name=License Content
+param.license-text.description=License Content
+param.xdg-prefix.name=Prefix for XDG files (mime, desktop)
+param.xdg-prefix.description=Prefix for XDG MimeInfo and Desktop Files.  Defaults to <vendor>-<appName>, with spaces dropped.
+param.rpm-app-bundler.name=RPM Bundler
+param.rpm-app-bundler.description=RPM Bundler
+param.bundle-name.name=RPM Bundle
+param.bundle-name.description=RPM Bundle
+
+resource.deb-control-file=DEB control file
+resource.deb-preinstall-script=DEB preinstall script
+resource.deb-prerm-script=DEB prerm script
+resource.deb-postinstall-script=DEB postinstall script
+resource.deb-postrm-script=DEB postrm script
+resource.deb-copyright-file=DEB copyright file
+resource.deb-init-script=DEB init script
+resource.menu-shortcut-descriptor=Menu shortcut descriptor
+resource.menu-icon=menu icon
+resource.rpm-spec-file=RPM spec file
+resource.rpm-init-script=RPM init script
+
+
+error.parameters-null=Parameters map is null.
+error.parameters-null.advice=Pass in a non-null parameters map.
+error.parameters-null=Parameters map is null.
+error.parameters-null.advice=Pass in a non-null parameters map.
+error.tool-not-found=Can not find {0}.
+error.tool-not-found.advice=Please install required packages.
+error.launcher-name-too-long=The bundle name "{0}" is too long for a daemon.
+error.launcher-name-too-long.advice=Set a bundler argument "{0}" to a bundle name that is shorter than 16 characters.
+error.cannot-create-output-dir=Output directory {0} cannot be created.
+error.cannot-write-to-output-dir=Output directory {0} is not writable.
+error.no-content-types-for-file-association=No MIME types were specified for File Association number {0}.
+error.no-content-types-for-file-association.advice=For Linux Bundling specify one and only one MIME type for each file association.
+error.too-many-content-types-for-file-association=More than one MIME types was specified for File Association number {0}.
+error.too-many-content-types-for-file-association.advice=For Linux Bundling specify one and only one MIME type for each file association.
+error.no-support-for-peruser-daemons=Bundler doesn't support per-user daemons.
+error.no-support-for-peruser-daemons.advice=Make sure that the system wide hint is set to true.
+error.invalid-value-for-package-name=Invalid value "{0}" for the package name.
+error.invalid-value-for-package-name.advice=Set the "linux.bundleName" parameter to a valid Debian package name. Note that the package names must consist only of lower case letters (a-z), digits (0-9), plus (+) and minus (-) signs, and periods (.). They must be at least two characters long and must start with an alphanumeric character.
+error.cannot-find-rpmbuild=Can not find rpmbuild {0} or newer.
+error.cannot-find-rpmbuild.advice=\  Install packages needed to build RPM, version {0} or newer.
+
+
+message.icon-not-png=The specified icon "{0}" is not a PNG file and will not be used.  The default icon will be used in it's place.
+message.test-for-tool=Test for [{0}]. Result\: {1}
+message.outputting-to-location=Generating DEB for installer to\: {0}
+message.output-to-location=Package (.deb) saved to\: {0}
+message.debs-like-licenses=Debian packages should specify a license.  The absence of a license will cause some linux distributions to complain about the quality of the application.
+message.one-shortcut-required=At least one type of shortcut is required. Enabling menu shortcut.
+message.outputting-bundle-location=Generating RPM for installer to\: {0}
+message.output-bundle-location=Package (.rpm) saved to\: {0}
+message.creating-association-with-null-extension=Creating association with null extension.
Binary file src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/javalogo_white_32.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.control	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,10 @@
+Package: APPLICATION_PACKAGE
+Version: APPLICATION_VERSION
+Section: unknown
+Maintainer: APPLICATION_MAINTAINER
+Priority: optional
+Architecture: APPLICATION_ARCH
+Provides: APPLICATION_PACKAGE
+Description: APPLICATION_SUMMARY
+Installed-Size: APPLICATION_INSTALLED_SIZE
+PACKAGE_DEPENDENCIES
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.copyright	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,8 @@
+
+Copyright:
+
+    APPLICATION_COPYRIGHT
+
+License:
+
+    APPLICATION_LICENSE_TEXT
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.desktop	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,9 @@
+[Desktop Entry]
+Name=APPLICATION_NAME
+Comment=APPLICATION_SUMMARY
+Exec=INSTALLATION_DIRECTORY/APPLICATION_FS_NAME/APPLICATION_LAUNCHER_FILENAME
+Icon=INSTALLATION_DIRECTORY/APPLICATION_FS_NAME/APPLICATION_LAUNCHER_FILENAME.png
+Terminal=false
+Type=Application
+Categories=DEPLOY_BUNDLE_CATEGORY
+DESKTOP_MIMES
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.postinst	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,61 @@
+#!/bin/sh
+# postinst script for APPLICATION_NAME
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+#        * <postinst> `configure' <most-recently-configured-version>
+#        * <old-postinst> `abort-upgrade' <new version>
+#        * <conflictor's-postinst> `abort-remove' `in-favour' <package>
+#          <new-version>
+#        * <postinst> `abort-remove'
+#        * <deconfigured's-postinst> `abort-deconfigure' `in-favour'
+#          <failed-install-package> <version> `removing'
+#          <conflicting-package> <version>
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+
+case "$1" in
+    configure)
+        if [ "RUNTIME_INSTALLER" != "true" ]; then
+            echo Adding shortcut to the menu
+SECONDARY_LAUNCHERS_INSTALL
+            xdg-desktop-menu install --novendor INSTALLATION_DIRECTORY/APPLICATION_FS_NAME/APPLICATION_LAUNCHER_FILENAME.desktop
+FILE_ASSOCIATION_INSTALL
+        fi
+        if [ "SERVICE_HINT" = "true" ]; then
+            echo Installing daemon
+            cp INSTALLATION_DIRECTORY/APPLICATION_FS_NAME/APPLICATION_PACKAGE.init /etc/init.d/APPLICATION_PACKAGE
+
+            if [ -x "/etc/init.d/APPLICATION_PACKAGE" ]; then
+                update-rc.d APPLICATION_PACKAGE defaults
+
+                if [ "START_ON_INSTALL" = "true" ]; then
+                    if which invoke-rc.d >/dev/null 2>&1; then
+                        invoke-rc.d APPLICATION_PACKAGE start
+                    else
+                        /etc/init.d/APPLICATION_PACKAGE start
+                    fi
+                fi
+	        fi
+
+        fi
+    ;;
+
+    abort-upgrade|abort-remove|abort-deconfigure)
+    ;;
+
+    *)
+        echo "postinst called with unknown argument \`$1'" >&2
+        exit 1
+    ;;
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.postrm	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,44 @@
+#!/bin/sh
+# postrm script for APPLICATION_NAME
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+#        * <postrm> `remove'
+#        * <postrm> `purge'
+#        * <old-postrm> `upgrade' <new-version>
+#        * <new-postrm> `failed-upgrade' <old-version>
+#        * <new-postrm> `abort-install'
+#        * <new-postrm> `abort-install' <old-version>
+#        * <new-postrm> `abort-upgrade' <old-version>
+#        * <disappearer's-postrm> `disappear' <overwriter>
+#          <overwriter-version>
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+
+case "$1" in
+    purge|remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
+        if [ "$1" = "purge" ] ; then
+            if [ "SERVICE_HINT" = "true" ]; then
+                echo Uninstalling daemon
+                rm -f /etc/init.d/APPLICATION_PACKAGE
+
+                update-rc.d APPLICATION_PACKAGE remove
+            fi
+        fi
+    ;;
+
+    *)
+        echo "postrm called with unknown argument \`$1'" >&2
+        exit 1
+    ;;
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.preinst	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,35 @@
+#!/bin/sh
+# preinst script for APPLICATION_NAME
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+#        * <new-preinst> `install'
+#        * <new-preinst> `install' <old-version>
+#        * <new-preinst> `upgrade' <old-version>
+#        * <old-preinst> `abort-upgrade' <new-version>
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+
+
+case "$1" in
+    install|upgrade)
+    ;;
+
+    abort-upgrade)
+    ;;
+
+    *)
+        echo "preinst called with unknown argument \`$1'" >&2
+        exit 1
+    ;;
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.prerm	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,45 @@
+#!/bin/sh
+# prerm script for APPLICATION_NAME
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+#        * <prerm> `remove'
+#        * <old-prerm> `upgrade' <new-version>
+#        * <new-prerm> `failed-upgrade' <old-version>
+#        * <conflictor's-prerm> `remove' `in-favour' <package> <new-version>
+#        * <deconfigured's-prerm> `deconfigure' `in-favour'
+#          <package-being-installed> <version> `removing'
+#          <conflicting-package> <version>
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+
+
+case "$1" in
+    remove|upgrade|deconfigure)
+        if [ "RUNTIME_INSTALLER" != "true" ]; then
+            echo Removing shortcut
+SECONDARY_LAUNCHERS_REMOVE
+            xdg-desktop-menu uninstall --novendor INSTALLATION_DIRECTORY/APPLICATION_FS_NAME/APPLICATION_LAUNCHER_FILENAME.desktop
+FILE_ASSOCIATION_REMOVE
+        fi
+    ;;
+
+    failed-upgrade)
+    ;;
+
+    *)
+        echo "prerm called with unknown argument \`$1'" >&2
+        exit 1
+    ;;
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.spec	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,60 @@
+Summary: APPLICATION_SUMMARY
+Name: APPLICATION_PACKAGE
+Version: APPLICATION_VERSION
+Release: 1
+License: APPLICATION_LICENSE_TYPE
+Vendor: APPLICATION_VENDOR
+Prefix: INSTALLATION_DIRECTORY
+Provides: APPLICATION_PACKAGE
+Autoprov: 0
+Autoreq: 0
+PACKAGE_DEPENDENCIES
+
+#avoid ARCH subfolder
+%define _rpmfilename %%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm
+
+#comment line below to enable effective jar compression
+#it could easily get your package size from 40 to 15Mb but 
+#build time will substantially increase and it may require unpack200/system java to install
+%define __jar_repack %{nil}
+
+%description
+APPLICATION_DESCRIPTION
+
+%prep
+
+%build
+
+%install
+rm -rf %{buildroot}
+mkdir -p %{buildroot}INSTALLATION_DIRECTORY
+cp -r %{_sourcedir}/APPLICATION_FS_NAME %{buildroot}INSTALLATION_DIRECTORY
+
+%files
+APPLICATION_LICENSE_FILE
+INSTALLATION_DIRECTORY/APPLICATION_FS_NAME
+
+%post
+if [ "RUNTIME_INSTALLER" != "true" ]; then
+SECONDARY_LAUNCHERS_INSTALL
+    xdg-desktop-menu install --novendor INSTALLATION_DIRECTORY/APPLICATION_FS_NAME/APPLICATION_LAUNCHER_FILENAME.desktop
+FILE_ASSOCIATION_INSTALL
+fi
+
+%preun
+if [ "RUNTIME_INSTALLER" != "true" ]; then
+SECONDARY_LAUNCHERS_REMOVE
+    xdg-desktop-menu uninstall --novendor INSTALLATION_DIRECTORY/APPLICATION_FS_NAME/APPLICATION_LAUNCHER_FILENAME.desktop
+FILE_ASSOCIATION_REMOVE
+fi
+if [ "SERVICE_HINT" = "true" ]; then
+    if [ -x "/etc/init.d/APPLICATION_PACKAGE" ]; then
+        if [ "STOP_ON_UNINSTALL" = "true" ]; then
+            /etc/init.d/APPLICATION_PACKAGE stop
+        fi
+        /sbin/chkconfig --del APPLICATION_PACKAGE
+        rm -f /etc/init.d/APPLICATION_PACKAGE
+    fi
+fi
+
+%clean
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/linux/classes/module-info.java.extra	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+provides jdk.jpackage.internal.Bundler with
+    jdk.jpackage.internal.LinuxAppBundler,
+    jdk.jpackage.internal.LinuxDebBundler,
+    jdk.jpackage.internal.LinuxRpmBundler;
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/linux/native/jpackageapplauncher/launcher.cpp	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2014, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include <dlfcn.h>
+#include <locale.h>
+#include <string>
+#include <libgen.h>
+#include <stdio.h>
+#include <unistd.h>
+
+
+typedef bool (*start_launcher)(int argc, char* argv[]);
+typedef void (*stop_launcher)();
+
+#define MAX_PATH 1024
+
+std::string GetProgramPath() {
+    ssize_t len = 0;
+    std::string result;
+    char buffer[MAX_PATH] = {0};
+
+    if ((len = readlink("/proc/self/exe", buffer, MAX_PATH - 1)) != -1) {
+        buffer[len] = '\0';
+        result = buffer;
+    }
+
+    return result;
+}
+
+int main(int argc, char *argv[]) {
+    int result = 1;
+    setlocale(LC_ALL, "en_US.utf8");
+    void* library = NULL;
+
+    {
+        std::string programPath = GetProgramPath();
+        std::string libraryName = dirname((char*)programPath.c_str());
+        libraryName += "/libapplauncher.so";
+        library = dlopen(libraryName.c_str(), RTLD_LAZY);
+
+        if (library == NULL) {
+            fprintf(stderr, "dlopen failed: %s\n", dlerror());
+            fprintf(stderr, "%s not found.\n", libraryName.c_str());
+        }
+    }
+
+    if (library != NULL) {
+        start_launcher start = (start_launcher)dlsym(library, "start_launcher");
+        stop_launcher stop = (stop_launcher)dlsym(library, "stop_launcher");
+
+        if (start != NULL && stop != NULL) {
+            if (start(argc, argv) == true) {
+                result = 0;
+                stop();
+            }
+        } else {
+            fprintf(stderr, "cannot find start_launcher and stop_launcher in libapplauncher.so");
+        }
+
+        dlclose(library);
+    }
+
+
+    return result;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/linux/native/libapplauncher/LinuxPlatform.cpp	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,1087 @@
+/*
+ * Copyright (c) 2014, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "Platform.h"
+
+#include "JavaVirtualMachine.h"
+#include "LinuxPlatform.h"
+#include "PlatformString.h"
+#include "IniFile.h"
+#include "Helpers.h"
+#include "FilePath.h"
+
+#include <stdlib.h>
+#include <pwd.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <limits.h>
+#include <signal.h>
+
+#define LINUX_JPACKAGE_TMP_DIR "/.java/jpackage/tmp"
+
+TString GetEnv(const TString &name) {
+    TString result;
+
+    char *value = ::getenv((TCHAR*) name.c_str());
+
+    if (value != NULL) {
+        result = value;
+    }
+
+    return result;
+}
+
+LinuxPlatform::LinuxPlatform(void) : Platform(),
+PosixPlatform() {
+    FMainThread = pthread_self();
+}
+
+LinuxPlatform::~LinuxPlatform(void) {
+}
+
+TString LinuxPlatform::GetPackageAppDirectory() {
+    return FilePath::IncludeTrailingSeparator(
+            GetPackageRootDirectory()) + _T("app");
+}
+
+TString LinuxPlatform::GetAppName() {
+    TString result = GetModuleFileName();
+    result = FilePath::ExtractFileName(result);
+    return result;
+}
+
+TString LinuxPlatform::GetPackageLauncherDirectory() {
+    return GetPackageRootDirectory();
+}
+
+TString LinuxPlatform::GetPackageRuntimeBinDirectory() {
+    return FilePath::IncludeTrailingSeparator(GetPackageRootDirectory()) + _T("runtime/bin");
+}
+
+void LinuxPlatform::ShowMessage(TString title, TString description) {
+    printf("%s %s\n", PlatformString(title).toPlatformString(),
+            PlatformString(description).toPlatformString());
+    fflush(stdout);
+}
+
+void LinuxPlatform::ShowMessage(TString description) {
+    TString appname = GetModuleFileName();
+    appname = FilePath::ExtractFileName(appname);
+    ShowMessage(PlatformString(appname).toPlatformString(),
+            PlatformString(description).toPlatformString());
+}
+
+TCHAR* LinuxPlatform::ConvertStringToFileSystemString(TCHAR* Source,
+        bool &release) {
+    // Not Implemented.
+    return NULL;
+}
+
+TCHAR* LinuxPlatform::ConvertFileSystemStringToString(TCHAR* Source,
+        bool &release) {
+    // Not Implemented.
+    return NULL;
+}
+
+TString LinuxPlatform::GetModuleFileName() {
+    ssize_t len = 0;
+    TString result;
+    DynamicBuffer<TCHAR> buffer(MAX_PATH);
+    if (buffer.GetData() == NULL) {
+        return result;
+    }
+
+    if ((len = readlink("/proc/self/exe", buffer.GetData(),
+            MAX_PATH - 1)) != -1) {
+        buffer[len] = '\0';
+        result = buffer.GetData();
+    }
+
+    return result;
+}
+
+void LinuxPlatform::SetCurrentDirectory(TString Value) {
+    chdir(PlatformString(Value).toPlatformString());
+}
+
+TString LinuxPlatform::GetPackageRootDirectory() {
+    TString filename = GetModuleFileName();
+    return FilePath::ExtractFilePath(filename);
+}
+
+TString LinuxPlatform::GetAppDataDirectory() {
+    TString result;
+    TString home = GetEnv(_T("HOME"));
+
+    if (home.empty() == false) {
+        result += FilePath::IncludeTrailingSeparator(home) + _T(".local");
+    }
+
+    return result;
+}
+
+ISectionalPropertyContainer* LinuxPlatform::GetConfigFile(TString FileName) {
+    IniFile *result = new IniFile();
+    if (result == NULL) {
+        return NULL;
+    }
+
+    if (result->LoadFromFile(FileName) == false) {
+        // New property file format was not found,
+        // attempt to load old property file format.
+        Helpers::LoadOldConfigFile(FileName, result);
+    }
+
+    return result;
+}
+
+TString LinuxPlatform::GetBundledJVMLibraryFileName(TString RuntimePath) {
+    TString result = FilePath::IncludeTrailingSeparator(RuntimePath) +
+            "lib/libjli.so";
+
+    if (FilePath::FileExists(result) == false) {
+        result = FilePath::IncludeTrailingSeparator(RuntimePath) +
+                "lib/jli/libjli.so";
+        if (FilePath::FileExists(result) == false) {
+            printf("Cannot find libjli.so!");
+        }
+    }
+
+    return result;
+}
+
+bool LinuxPlatform::IsMainThread() {
+    bool result = (FMainThread == pthread_self());
+    return result;
+}
+
+TString LinuxPlatform::getTmpDirString() {
+    return TString(LINUX_JPACKAGE_TMP_DIR);
+}
+
+TPlatformNumber LinuxPlatform::GetMemorySize() {
+    long pages = sysconf(_SC_PHYS_PAGES);
+    long page_size = sysconf(_SC_PAGE_SIZE);
+    TPlatformNumber result = pages * page_size;
+    result = result / 1048576; // Convert from bytes to megabytes.
+    return result;
+}
+
+void PosixProcess::Cleanup() {
+    if (FOutputHandle != 0) {
+        close(FOutputHandle);
+        FOutputHandle = 0;
+    }
+
+    if (FInputHandle != 0) {
+        close(FInputHandle);
+        FInputHandle = 0;
+    }
+}
+
+#define PIPE_READ 0
+#define PIPE_WRITE 1
+
+bool PosixProcess::Execute(const TString Application,
+        const std::vector<TString> Arguments, bool AWait) {
+    bool result = false;
+
+    if (FRunning == false) {
+        FRunning = true;
+
+        int handles[2];
+
+        if (pipe(handles) == -1) {
+            return false;
+        }
+
+        struct sigaction sa;
+        sa.sa_handler = SIG_IGN;
+        sigemptyset(&sa.sa_mask);
+        sa.sa_flags = 0;
+
+        FChildPID = fork();
+
+        // PID returned by vfork is 0 for the child process and the
+        // PID of the child process for the parent.
+        if (FChildPID == -1) {
+            // Error
+            TString message = PlatformString::Format(
+                    _T("Error: Unable to create process %s"),
+                    Application.data());
+            throw Exception(message);
+        } else if (FChildPID == 0) {
+            Cleanup();
+            TString command = Application;
+
+            for (std::vector<TString>::const_iterator iterator =
+                    Arguments.begin(); iterator != Arguments.end();
+                    iterator++) {
+                command += TString(_T(" ")) + *iterator;
+            }
+#ifdef DEBUG
+            printf("%s\n", command.data());
+#endif // DEBUG
+
+            dup2(handles[PIPE_READ], STDIN_FILENO);
+            dup2(handles[PIPE_WRITE], STDOUT_FILENO);
+
+            close(handles[PIPE_READ]);
+            close(handles[PIPE_WRITE]);
+
+            execl("/bin/sh", "sh", "-c", command.data(), (char *) 0);
+
+            _exit(127);
+        } else {
+            FOutputHandle = handles[PIPE_READ];
+            FInputHandle = handles[PIPE_WRITE];
+
+            if (AWait == true) {
+                ReadOutput();
+                Wait();
+                Cleanup();
+                FRunning = false;
+                result = true;
+            } else {
+                result = true;
+            }
+        }
+    }
+
+    return result;
+}
+
+
+//----------------------------------------------------------------------------
+
+#ifndef __UNIX_JPACKAGE_PLATFORM__
+#define __UNIX_JPACKAGE_PLATFORM__
+
+/** Provide an abstraction for difference in the platform APIs,
+     e.g. string manipulation functions, etc. */
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/stat.h>
+
+#define TCHAR char
+
+#define _T(x) x
+
+#define JPACKAGE_MULTIBYTE_SNPRINTF snprintf
+
+#define JPACKAGE_SNPRINTF(buffer, sizeOfBuffer, count, format, ...) \
+    snprintf((buffer), (count), (format), __VA_ARGS__)
+
+#define JPACKAGE_PRINTF(format, ...) \
+    printf((format), ##__VA_ARGS__)
+
+#define JPACKAGE_FPRINTF(dest, format, ...) \
+    fprintf((dest), (format), __VA_ARGS__)
+
+#define JPACKAGE_SSCANF(buf, format, ...) \
+    sscanf((buf), (format), __VA_ARGS__)
+
+#define JPACKAGE_STRDUP(strSource) \
+    strdup((strSource))
+
+//return "error code" (like on Windows)
+
+static int JPACKAGE_STRNCPY(char *strDest, size_t numberOfElements,
+        const char *strSource, size_t count) {
+    char *s = strncpy(strDest, strSource, count);
+    // Duplicate behavior of the Windows' _tcsncpy_s() by adding a NULL
+    // terminator at the end of the string.
+    if (count < numberOfElements) {
+        s[count] = '\0';
+    } else {
+        s[numberOfElements - 1] = '\0';
+    }
+    return (s == strDest) ? 0 : 1;
+}
+
+#define JPACKAGE_STRICMP(x, y) \
+    strcasecmp((x), (y))
+
+#define JPACKAGE_STRNICMP(x, y, cnt) \
+    strncasecmp((x), (y), (cnt))
+
+#define JPACKAGE_STRNCMP(x, y, cnt) \
+    strncmp((x), (y), (cnt))
+
+#define JPACKAGE_STRLEN(x) \
+    strlen((x))
+
+#define JPACKAGE_STRSTR(x, y) \
+    strstr((x), (y))
+
+#define JPACKAGE_STRCHR(x, y) \
+    strchr((x), (y))
+
+#define JPACKAGE_STRRCHR(x, y) \
+    strrchr((x), (y))
+
+#define JPACKAGE_STRPBRK(x, y) \
+    strpbrk((x), (y))
+
+#define JPACKAGE_GETENV(x) \
+    getenv((x))
+
+#define JPACKAGE_PUTENV(x) \
+    putenv((x))
+
+#define JPACKAGE_STRCMP(x, y) \
+    strcmp((x), (y))
+
+#define JPACKAGE_STRCPY(x, y) \
+    strcpy((x), (y))
+
+#define JPACKAGE_STRCAT(x, y) \
+    strcat((x), (y))
+
+#define JPACKAGE_ATOI(x) \
+    atoi((x))
+
+#define JPACKAGE_FOPEN(x, y) \
+    fopen((x), (y))
+
+#define JPACKAGE_FGETS(x, y, z) \
+    fgets((x), (y), (z))
+
+#define JPACKAGE_REMOVE(x) \
+    remove((x))
+
+#define JPACKAGE_SPAWNV(mode, cmd, args) \
+    spawnv((mode), (cmd), (args))
+
+#define JPACKAGE_ISDIGIT(ch) isdigit(ch)
+
+// for non-unicode, just return the input string for
+// the following 2 conversions
+#define JPACKAGE_NEW_MULTIBYTE(message) message
+
+#define JPACKAGE_NEW_FROM_MULTIBYTE(message) message
+
+// for non-unicode, no-op for the relase operation
+// since there is no memory allocated for the
+// string conversions
+#define JPACKAGE_RELEASE_MULTIBYTE(tmpMBCS)
+
+#define JPACKAGE_RELEASE_FROM_MULTIBYTE(tmpMBCS)
+
+// The size will be used for converting from 1 byte to 1 byte encoding.
+// Ensure have space for zero-terminator.
+#define JPACKAGE_GET_SIZE_FOR_ENCODING(message, theLength) (theLength + 1)
+
+#endif
+#define xmlTagType    0
+#define xmlPCDataType 1
+
+typedef struct _xmlNode XMLNode;
+typedef struct _xmlAttribute XMLAttribute;
+
+struct _xmlNode {
+    int _type; // Type of node: tag, pcdata, cdate
+    TCHAR* _name; // Contents of node
+    XMLNode* _next; // Next node at same level
+    XMLNode* _sub; // First sub-node
+    XMLAttribute* _attributes; // List of attributes
+};
+
+struct _xmlAttribute {
+    TCHAR* _name; // Name of attribute
+    TCHAR* _value; // Value of attribute
+    XMLAttribute* _next; // Next attribute for this tag
+};
+
+// Public interface
+static void RemoveNonAsciiUTF8FromBuffer(char *buf);
+XMLNode* ParseXMLDocument(TCHAR* buf);
+void FreeXMLDocument(XMLNode* root);
+
+// Utility methods for parsing document
+XMLNode* FindXMLChild(XMLNode* root, const TCHAR* name);
+TCHAR* FindXMLAttribute(XMLAttribute* attr, const TCHAR* name);
+
+// Debugging
+void PrintXMLDocument(XMLNode* node, int indt);
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <setjmp.h>
+#include <stdlib.h>
+#include <wctype.h>
+
+#define JWS_assert(s, msg)      \
+    if (!(s)) { Abort(msg); }
+
+
+// Internal declarations
+static XMLNode* ParseXMLElement(void);
+static XMLAttribute* ParseXMLAttribute(void);
+static TCHAR* SkipWhiteSpace(TCHAR *p);
+static TCHAR* SkipXMLName(TCHAR *p);
+static TCHAR* SkipXMLComment(TCHAR *p);
+static TCHAR* SkipXMLDocType(TCHAR *p);
+static TCHAR* SkipXMLProlog(TCHAR *p);
+static TCHAR* SkipPCData(TCHAR *p);
+static int IsPCData(TCHAR *p);
+static void ConvertBuiltInEntities(TCHAR* p);
+static void SetToken(int type, TCHAR* start, TCHAR* end);
+static void GetNextToken(void);
+static XMLNode* CreateXMLNode(int type, TCHAR* name);
+static XMLAttribute* CreateXMLAttribute(TCHAR *name, TCHAR* value);
+static XMLNode* ParseXMLElement(void);
+static XMLAttribute* ParseXMLAttribute(void);
+static void FreeXMLAttribute(XMLAttribute* attr);
+static void PrintXMLAttributes(XMLAttribute* attr);
+static void indent(int indt);
+
+static jmp_buf jmpbuf;
+static XMLNode* root_node = NULL;
+
+/** definition of error codes for setjmp/longjmp,
+ *  that can be handled in ParseXMLDocument()
+ */
+#define JMP_NO_ERROR     0
+#define JMP_OUT_OF_RANGE 1
+
+#define NEXT_CHAR(p) { \
+    if (*p != 0) { \
+        p++; \
+    } else { \
+        longjmp(jmpbuf, JMP_OUT_OF_RANGE); \
+    } \
+}
+#define NEXT_CHAR_OR_BREAK(p) { \
+    if (*p != 0) { \
+        p++; \
+    } else { \
+        break; \
+    } \
+}
+#define NEXT_CHAR_OR_RETURN(p) { \
+    if (*p != 0) { \
+        p++; \
+    } else { \
+        return; \
+    } \
+}
+#define SKIP_CHARS(p,n) { \
+    int i; \
+    for (i = 0; i < (n); i++) { \
+        if (*p != 0) { \
+            p++; \
+        } else { \
+           longjmp(jmpbuf, JMP_OUT_OF_RANGE); \
+        } \
+    } \
+}
+#define SKIP_CHARS_OR_BREAK(p,n) { \
+    int i; \
+    for (i = 0; i < (n); i++) { \
+        if (*p != 0) { \
+            p++; \
+        } else { \
+            break; \
+        } \
+    } \
+    if (i < (n)) { \
+        break; \
+    } \
+}
+
+/** Iterates through the null-terminated buffer (i.e., C string) and
+ *  replaces all UTF-8 encoded character >255 with 255
+ *
+ *  UTF-8 encoding:
+ *
+ *   Range A:  0x0000 - 0x007F
+ *                               0 | bits 0 - 7
+ *   Range B : 0x0080 - 0x07FF  :
+ *                               110 | bits 6 - 10
+ *                               10  | bits 0 - 5
+ *   Range C : 0x0800 - 0xFFFF  :
+ *                               1110 | bits 12-15
+ *                               10   | bits  6-11
+ *                               10   | bits  0-5
+ */
+static void RemoveNonAsciiUTF8FromBuffer(char *buf) {
+    char* p;
+    char* q;
+    char c;
+    p = q = buf;
+    // We are not using NEXT_CHAR() to check if *q is NULL, as q is output
+    // location and offset for q is smaller than for p.
+    while (*p != '\0') {
+        c = *p;
+        if ((c & 0x80) == 0) {
+            /* Range A */
+            *q++ = *p;
+            NEXT_CHAR(p);
+        } else if ((c & 0xE0) == 0xC0) {
+            /* Range B */
+            *q++ = (char) 0xFF;
+            NEXT_CHAR(p);
+            NEXT_CHAR_OR_BREAK(p);
+        } else {
+            /* Range C */
+            *q++ = (char) 0xFF;
+            NEXT_CHAR(p);
+            SKIP_CHARS_OR_BREAK(p, 2);
+        }
+    }
+    /* Null terminate string */
+    *q = '\0';
+}
+
+static TCHAR* SkipWhiteSpace(TCHAR *p) {
+    if (p != NULL) {
+        while (iswspace(*p))
+            NEXT_CHAR_OR_BREAK(p);
+    }
+    return p;
+}
+
+static TCHAR* SkipXMLName(TCHAR *p) {
+    TCHAR c = *p;
+    /* Check if start of token */
+    if (('a' <= c && c <= 'z') ||
+            ('A' <= c && c <= 'Z') ||
+            c == '_' || c == ':') {
+
+        while (('a' <= c && c <= 'z') ||
+                ('A' <= c && c <= 'Z') ||
+                ('0' <= c && c <= '9') ||
+                c == '_' || c == ':' || c == '.' || c == '-') {
+            NEXT_CHAR(p);
+            c = *p;
+            if (c == '\0') break;
+        }
+    }
+    return p;
+}
+
+static TCHAR* SkipXMLComment(TCHAR *p) {
+    if (p != NULL) {
+        if (JPACKAGE_STRNCMP(p, _T("<!--"), 4) == 0) {
+            SKIP_CHARS(p, 4);
+            do {
+                if (JPACKAGE_STRNCMP(p, _T("-->"), 3) == 0) {
+                    SKIP_CHARS(p, 3);
+                    return p;
+                }
+                NEXT_CHAR(p);
+            } while (*p != '\0');
+        }
+    }
+    return p;
+}
+
+static TCHAR* SkipXMLDocType(TCHAR *p) {
+    if (p != NULL) {
+        if (JPACKAGE_STRNCMP(p, _T("<!"), 2) == 0) {
+            SKIP_CHARS(p, 2);
+            while (*p != '\0') {
+                if (*p == '>') {
+                    NEXT_CHAR(p);
+                    return p;
+                }
+                NEXT_CHAR(p);
+            }
+        }
+    }
+    return p;
+}
+
+static TCHAR* SkipXMLProlog(TCHAR *p) {
+    if (p != NULL) {
+        if (JPACKAGE_STRNCMP(p, _T("<?"), 2) == 0) {
+            SKIP_CHARS(p, 2);
+            do {
+                if (JPACKAGE_STRNCMP(p, _T("?>"), 2) == 0) {
+                    SKIP_CHARS(p, 2);
+                    return p;
+                }
+                NEXT_CHAR(p);
+            } while (*p != '\0');
+        }
+    }
+    return p;
+}
+
+/* Search for the built-in XML entities:
+ * &amp; (&), &lt; (<), &gt; (>), &apos; ('), and &quote(")
+ * and convert them to a real TCHARacter
+ */
+static void ConvertBuiltInEntities(TCHAR* p) {
+    TCHAR* q;
+    q = p;
+    // We are not using NEXT_CHAR() to check if *q is NULL,
+    // as q is output location and offset for q is smaller than for p.
+    while (*p) {
+        if (IsPCData(p)) {
+            /* dont convert &xxx values within PData */
+            TCHAR *end;
+            end = SkipPCData(p);
+            while (p < end) {
+                *q++ = *p;
+                NEXT_CHAR(p);
+            }
+        } else {
+            if (JPACKAGE_STRNCMP(p, _T("&amp;"), 5) == 0) {
+                *q++ = '&';
+                SKIP_CHARS(p, 5);
+            } else if (JPACKAGE_STRNCMP(p, _T("&lt;"), 4) == 0) {
+                *q = '<';
+                SKIP_CHARS(p, 4);
+            } else if (JPACKAGE_STRNCMP(p, _T("&gt;"), 4) == 0) {
+                *q = '>';
+                SKIP_CHARS(p, 4);
+            } else if (JPACKAGE_STRNCMP(p, _T("&apos;"), 6) == 0) {
+                *q = '\'';
+                SKIP_CHARS(p, 6);
+            } else if (JPACKAGE_STRNCMP(p, _T("&quote;"), 7) == 0) {
+                *q = '\"';
+                SKIP_CHARS(p, 7);
+            } else {
+                *q++ = *p;
+                NEXT_CHAR(p);
+            }
+        }
+    }
+    *q = '\0';
+}
+
+/* ------------------------------------------------------------- */
+/* XML tokenizer */
+
+#define TOKEN_UNKNOWN             0
+#define TOKEN_BEGIN_TAG           1  /* <tag */
+#define TOKEN_END_TAG             2  /* </tag */
+#define TOKEN_CLOSE_BRACKET       3  /* >  */
+#define TOKEN_EMPTY_CLOSE_BRACKET 4  /* /> */
+#define TOKEN_PCDATA              5  /* pcdata */
+#define TOKEN_CDATA               6  /* cdata */
+#define TOKEN_EOF                 7
+
+static TCHAR* CurPos = NULL;
+static TCHAR* CurTokenName = NULL;
+static int CurTokenType;
+static int MaxTokenSize = -1;
+
+/* Copy token from buffer to Token variable */
+static void SetToken(int type, TCHAR* start, TCHAR* end) {
+    int len = end - start;
+    if (len > MaxTokenSize) {
+        if (CurTokenName != NULL) free(CurTokenName);
+        CurTokenName = (TCHAR *) malloc((len + 1) * sizeof (TCHAR));
+        if (CurTokenName == NULL) {
+            return;
+        }
+        MaxTokenSize = len;
+    }
+
+    CurTokenType = type;
+    JPACKAGE_STRNCPY(CurTokenName, len + 1, start, len);
+    CurTokenName[len] = '\0';
+}
+
+/* Skip XML comments, doctypes, and prolog tags */
+static TCHAR* SkipFilling(void) {
+    TCHAR *q = CurPos;
+
+    /* Skip white space and comment sections */
+    do {
+        q = CurPos;
+        CurPos = SkipWhiteSpace(CurPos);
+        CurPos = SkipXMLComment(CurPos); /* Must be called befor DocTypes */
+        CurPos = SkipXMLDocType(CurPos); /* <! ... > directives */
+        CurPos = SkipXMLProlog(CurPos); /* <? ... ?> directives */
+    } while (CurPos != q);
+
+    return CurPos;
+}
+
+/* Parses next token and initializes the global token variables above
+   The tokennizer automatically skips comments (<!-- comment -->) and
+   <! ... > directives.
+ */
+static void GetNextToken(void) {
+    TCHAR *p, *q;
+
+    /* Skip white space and comment sections */
+    p = SkipFilling();
+
+    if (p == NULL || *p == '\0') {
+        CurTokenType = TOKEN_EOF;
+        return;
+    } else if (p[0] == '<' && p[1] == '/') {
+        /* TOKEN_END_TAG */
+        q = SkipXMLName(p + 2);
+        SetToken(TOKEN_END_TAG, p + 2, q);
+        p = q;
+    } else if (*p == '<') {
+        /* TOKEN_BEGIN_TAG */
+        q = SkipXMLName(p + 1);
+        SetToken(TOKEN_BEGIN_TAG, p + 1, q);
+        p = q;
+    } else if (p[0] == '>') {
+        CurTokenType = TOKEN_CLOSE_BRACKET;
+        NEXT_CHAR(p);
+    } else if (p[0] == '/' && p[1] == '>') {
+        CurTokenType = TOKEN_EMPTY_CLOSE_BRACKET;
+        SKIP_CHARS(p, 2);
+    } else {
+        /* Search for end of data */
+        q = p + 1;
+        while (*q && *q != '<') {
+            if (IsPCData(q)) {
+                q = SkipPCData(q);
+            } else {
+                NEXT_CHAR(q);
+            }
+        }
+        SetToken(TOKEN_PCDATA, p, q);
+        /* Convert all entities inside token */
+        ConvertBuiltInEntities(CurTokenName);
+        p = q;
+    }
+    /* Advance pointer to beginning of next token */
+    CurPos = p;
+}
+
+static XMLNode* CreateXMLNode(int type, TCHAR* name) {
+    XMLNode* node;
+    node = (XMLNode*) malloc(sizeof (XMLNode));
+    if (node == NULL) {
+        return NULL;
+    }
+    node->_type = type;
+    node->_name = name;
+    node->_next = NULL;
+    node->_sub = NULL;
+    node->_attributes = NULL;
+    return node;
+}
+
+static XMLAttribute* CreateXMLAttribute(TCHAR *name, TCHAR* value) {
+    XMLAttribute* attr;
+    attr = (XMLAttribute*) malloc(sizeof (XMLAttribute));
+    if (attr == NULL) {
+        return NULL;
+    }
+    attr->_name = name;
+    attr->_value = value;
+    attr->_next = NULL;
+    return attr;
+}
+
+XMLNode* ParseXMLDocument(TCHAR* buf) {
+    XMLNode* root;
+    int err_code = setjmp(jmpbuf);
+    switch (err_code) {
+        case JMP_NO_ERROR:
+#ifndef _UNICODE
+            /* Remove UTF-8 encoding from buffer */
+            RemoveNonAsciiUTF8FromBuffer(buf);
+#endif
+
+            /* Get first Token */
+            CurPos = buf;
+            GetNextToken();
+
+            /* Parse document*/
+            root = ParseXMLElement();
+            break;
+        case JMP_OUT_OF_RANGE:
+            /* cleanup: */
+            if (root_node != NULL) {
+                FreeXMLDocument(root_node);
+                root_node = NULL;
+            }
+            if (CurTokenName != NULL) free(CurTokenName);
+            fprintf(stderr, "Error during parsing jnlp file...\n");
+            exit(-1);
+            break;
+        default:
+            root = NULL;
+            break;
+    }
+
+    return root;
+}
+
+static XMLNode* ParseXMLElement(void) {
+    XMLNode* node = NULL;
+    XMLNode* subnode = NULL;
+    XMLNode* nextnode = NULL;
+    XMLAttribute* attr = NULL;
+
+    if (CurTokenType == TOKEN_BEGIN_TAG) {
+
+        /* Create node for new element tag */
+        node = CreateXMLNode(xmlTagType, JPACKAGE_STRDUP(CurTokenName));
+        /* We need to save root node pointer to be able to cleanup
+           if an error happens during parsing */
+        if (!root_node) {
+            root_node = node;
+        }
+        /* Parse attributes. This section eats a all input until
+           EOF, a > or a /> */
+        attr = ParseXMLAttribute();
+        while (attr != NULL) {
+            attr->_next = node->_attributes;
+            node->_attributes = attr;
+            attr = ParseXMLAttribute();
+        }
+
+        /* This will eihter be a TOKEN_EOF, TOKEN_CLOSE_BRACKET, or a
+         * TOKEN_EMPTY_CLOSE_BRACKET */
+        GetNextToken();
+
+        /* Skip until '>', '/>' or EOF. This should really be an error, */
+        /* but we are loose */
+        //        if(CurTokenType == TOKEN_EMPTY_CLOSE_BRACKET ||
+        //               CurTokenType == TOKEN_CLOSE_BRACKET ||
+        //               CurTokenType  == TOKEN_EOF) {
+        //            println("XML Parsing error: wrong kind of token found");
+        //            return NULL;
+        //        }
+
+        if (CurTokenType == TOKEN_EMPTY_CLOSE_BRACKET) {
+            GetNextToken();
+            /* We are done with the sublevel - fall through to continue */
+            /* parsing tags at the same level */
+        } else if (CurTokenType == TOKEN_CLOSE_BRACKET) {
+            GetNextToken();
+
+            /* Parse until end tag if found */
+            node->_sub = ParseXMLElement();
+
+            if (CurTokenType == TOKEN_END_TAG) {
+                /* Find closing bracket '>' for end tag */
+                do {
+                    GetNextToken();
+                } while (CurTokenType != TOKEN_EOF &&
+                        CurTokenType != TOKEN_CLOSE_BRACKET);
+                GetNextToken();
+            }
+        }
+
+        /* Continue parsing rest on same level */
+        if (CurTokenType != TOKEN_EOF) {
+            /* Parse rest of stream at same level */
+            node->_next = ParseXMLElement();
+        }
+        return node;
+
+    } else if (CurTokenType == TOKEN_PCDATA) {
+        /* Create node for pcdata */
+        node = CreateXMLNode(xmlPCDataType, JPACKAGE_STRDUP(CurTokenName));
+        /* We need to save root node pointer to be able to cleanup
+           if an error happens during parsing */
+        if (!root_node) {
+            root_node = node;
+        }
+        GetNextToken();
+        return node;
+    }
+
+    /* Something went wrong. */
+    return NULL;
+}
+
+/* Parses an XML attribute. */
+static XMLAttribute* ParseXMLAttribute(void) {
+    TCHAR* q = NULL;
+    TCHAR* name = NULL;
+    TCHAR* PrevPos = NULL;
+
+    do {
+        /* We need to check this condition to avoid endless loop
+           in case if an error happend during parsing. */
+        if (PrevPos == CurPos) {
+            if (name != NULL) {
+                free(name);
+                name = NULL;
+            }
+
+            return NULL;
+        }
+
+        PrevPos = CurPos;
+
+        /* Skip whitespace etc. */
+        SkipFilling();
+
+        /* Check if we are done witht this attribute section */
+        if (CurPos[0] == '\0' ||
+                CurPos[0] == '>' ||
+                (CurPos[0] == '/' && CurPos[1] == '>')) {
+
+            if (name != NULL) {
+                free(name);
+                name = NULL;
+            }
+
+            return NULL;
+        }
+
+        /* Find end of name */
+        q = CurPos;
+        while (*q && !iswspace(*q) && *q != '=') NEXT_CHAR(q);
+
+        SetToken(TOKEN_UNKNOWN, CurPos, q);
+        if (name) {
+            free(name);
+            name = NULL;
+        }
+        name = JPACKAGE_STRDUP(CurTokenName);
+
+        /* Skip any whitespace */
+        CurPos = q;
+        CurPos = SkipFilling();
+
+        /* Next TCHARacter must be '=' for a valid attribute.
+           If it is not, this is really an error.
+           We ignore this, and just try to parse an attribute
+           out of the rest of the string.
+         */
+    } while (*CurPos != '=');
+
+    NEXT_CHAR(CurPos);
+    CurPos = SkipWhiteSpace(CurPos);
+    /* Parse CDATA part of attribute */
+    if ((*CurPos == '\"') || (*CurPos == '\'')) {
+        TCHAR quoteChar = *CurPos;
+        q = ++CurPos;
+        while (*q != '\0' && *q != quoteChar) NEXT_CHAR(q);
+        SetToken(TOKEN_CDATA, CurPos, q);
+        CurPos = q + 1;
+    } else {
+        q = CurPos;
+        while (*q != '\0' && !iswspace(*q)) NEXT_CHAR(q);
+        SetToken(TOKEN_CDATA, CurPos, q);
+        CurPos = q;
+    }
+
+    //Note: no need to free name and CurTokenName duplicate; they're assigned
+    // to an XMLAttribute structure in CreateXMLAttribute
+
+    return CreateXMLAttribute(name, JPACKAGE_STRDUP(CurTokenName));
+}
+
+void FreeXMLDocument(XMLNode* root) {
+    if (root == NULL) return;
+    FreeXMLDocument(root->_sub);
+    FreeXMLDocument(root->_next);
+    FreeXMLAttribute(root->_attributes);
+    free(root->_name);
+    free(root);
+}
+
+static void FreeXMLAttribute(XMLAttribute* attr) {
+    if (attr == NULL) return;
+    free(attr->_name);
+    free(attr->_value);
+    FreeXMLAttribute(attr->_next);
+    free(attr);
+}
+
+/* Find element at current level with a given name */
+XMLNode* FindXMLChild(XMLNode* root, const TCHAR* name) {
+    if (root == NULL) return NULL;
+
+    if (root->_type == xmlTagType && JPACKAGE_STRCMP(root->_name, name) == 0) {
+        return root;
+    }
+
+    return FindXMLChild(root->_next, name);
+}
+
+/* Search for an attribute with the given name and returns the contents. Returns NULL if
+ * attribute is not found
+ */
+TCHAR* FindXMLAttribute(XMLAttribute* attr, const TCHAR* name) {
+    if (attr == NULL) return NULL;
+    if (JPACKAGE_STRCMP(attr->_name, name) == 0) return attr->_value;
+    return FindXMLAttribute(attr->_next, name);
+}
+
+void PrintXMLDocument(XMLNode* node, int indt) {
+    if (node == NULL) return;
+
+    if (node->_type == xmlTagType) {
+        JPACKAGE_PRINTF(_T("\n"));
+        indent(indt);
+        JPACKAGE_PRINTF(_T("<%s"), node->_name);
+        PrintXMLAttributes(node->_attributes);
+        if (node->_sub == NULL) {
+            JPACKAGE_PRINTF(_T("/>\n"));
+        } else {
+            JPACKAGE_PRINTF(_T(">"));
+            PrintXMLDocument(node->_sub, indt + 1);
+            indent(indt);
+            JPACKAGE_PRINTF(_T("</%s>"), node->_name);
+        }
+    } else {
+        JPACKAGE_PRINTF(_T("%s"), node->_name);
+    }
+    PrintXMLDocument(node->_next, indt);
+}
+
+static void PrintXMLAttributes(XMLAttribute* attr) {
+    if (attr == NULL) return;
+
+    JPACKAGE_PRINTF(_T(" %s=\"%s\""), attr->_name, attr->_value);
+    PrintXMLAttributes(attr->_next);
+}
+
+static void indent(int indt) {
+    int i;
+    for (i = 0; i < indt; i++) {
+        JPACKAGE_PRINTF(_T("  "));
+    }
+}
+
+const TCHAR *CDStart = _T("<![CDATA[");
+const TCHAR *CDEnd = _T("]]>");
+
+static TCHAR* SkipPCData(TCHAR *p) {
+    TCHAR *end = JPACKAGE_STRSTR(p, CDEnd);
+    if (end != NULL) {
+        return end + sizeof (CDEnd);
+    }
+    return (++p);
+}
+
+static int IsPCData(TCHAR *p) {
+    const int size = sizeof (CDStart);
+    return (JPACKAGE_STRNCMP(CDStart, p, size) == 0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/linux/native/libapplauncher/LinuxPlatform.h	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2014, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef LINUXPLATFORM_H
+#define LINUXPLATFORM_H
+
+#include "Platform.h"
+#include "PosixPlatform.h"
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <pthread.h>
+#include <list>
+
+class LinuxPlatform : virtual public Platform, PosixPlatform {
+private:
+    pthread_t FMainThread;
+
+protected:
+    virtual TString getTmpDirString();
+
+public:
+    LinuxPlatform(void);
+    virtual ~LinuxPlatform(void);
+
+    TString GetPackageAppDirectory();
+    TString GetPackageLauncherDirectory();
+    TString GetPackageRuntimeBinDirectory();
+
+    virtual void ShowMessage(TString title, TString description);
+    virtual void ShowMessage(TString description);
+
+    virtual TCHAR* ConvertStringToFileSystemString(
+            TCHAR* Source, bool &release);
+    virtual TCHAR* ConvertFileSystemStringToString(
+            TCHAR* Source, bool &release);
+
+    virtual void SetCurrentDirectory(TString Value);
+    virtual TString GetPackageRootDirectory();
+    virtual TString GetAppDataDirectory();
+    virtual TString GetAppName();
+
+    virtual TString GetModuleFileName();
+
+    virtual TString GetBundledJVMLibraryFileName(TString RuntimePath);
+
+    virtual ISectionalPropertyContainer* GetConfigFile(TString FileName);
+
+    virtual bool IsMainThread();
+    virtual TPlatformNumber GetMemorySize();
+};
+
+#endif //LINUXPLATFORM_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/linux/native/libapplauncher/PlatformDefs.h	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef PLATFORM_DEFS_H
+#define PLATFORM_DEFS_H
+
+#include <errno.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <dlfcn.h>
+#include <libgen.h>
+#include <string>
+
+using namespace std;
+
+#ifndef LINUX
+#define LINUX
+#endif
+
+#define _T(x) x
+
+typedef char TCHAR;
+typedef std::string TString;
+#define StringLength strlen
+
+typedef unsigned long DWORD;
+
+#define TRAILING_PATHSEPARATOR '/'
+#define BAD_TRAILING_PATHSEPARATOR '\\'
+#define PATH_SEPARATOR ':'
+#define BAD_PATH_SEPARATOR ';'
+#define MAX_PATH 1000
+
+typedef long TPlatformNumber;
+typedef pid_t TProcessID;
+
+#define HMODULE void*
+
+typedef void* Module;
+typedef void* Procedure;
+
+#define StringToFileSystemString PlatformString
+#define FileSystemStringToString PlatformString
+
+#endif // PLATFORM_DEFS_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacAppBundler.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,423 @@
+/*
+ * Copyright (c) 2012, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jpackage.internal;
+
+import java.io.File;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.text.MessageFormat;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.ResourceBundle;
+
+import static jdk.jpackage.internal.StandardBundlerParam.*;
+import static jdk.jpackage.internal.MacBaseInstallerBundler.*;
+import jdk.jpackage.internal.AbstractAppImageBuilder;
+
+public class MacAppBundler extends AbstractImageBundler {
+
+    private static final ResourceBundle I18N = ResourceBundle.getBundle(
+            "jdk.jpackage.internal.resources.MacResources");
+
+    private static final String TEMPLATE_BUNDLE_ICON = "GenericApp.icns";
+
+    private static Map<String, String> getMacCategories() {
+        Map<String, String> map = new HashMap<>();
+        map.put("Business", "public.app-category.business");
+        map.put("Developer Tools", "public.app-category.developer-tools");
+        map.put("Education", "public.app-category.education");
+        map.put("Entertainment", "public.app-category.entertainment");
+        map.put("Finance", "public.app-category.finance");
+        map.put("Games", "public.app-category.games");
+        map.put("Graphics & Design", "public.app-category.graphics-design");
+        map.put("Healthcare & Fitness",
+                "public.app-category.healthcare-fitness");
+        map.put("Lifestyle", "public.app-category.lifestyle");
+        map.put("Medical", "public.app-category.medical");
+        map.put("Music", "public.app-category.music");
+        map.put("News", "public.app-category.news");
+        map.put("Photography", "public.app-category.photography");
+        map.put("Productivity", "public.app-category.productivity");
+        map.put("Reference", "public.app-category.reference");
+        map.put("Social Networking", "public.app-category.social-networking");
+        map.put("Sports", "public.app-category.sports");
+        map.put("Travel", "public.app-category.travel");
+        map.put("Utilities", "public.app-category.utilities");
+        map.put("Video", "public.app-category.video");
+        map.put("Weather", "public.app-category.weather");
+
+        map.put("Action Games", "public.app-category.action-games");
+        map.put("Adventure Games", "public.app-category.adventure-games");
+        map.put("Arcade Games", "public.app-category.arcade-games");
+        map.put("Board Games", "public.app-category.board-games");
+        map.put("Card Games", "public.app-category.card-games");
+        map.put("Casino Games", "public.app-category.casino-games");
+        map.put("Dice Games", "public.app-category.dice-games");
+        map.put("Educational Games", "public.app-category.educational-games");
+        map.put("Family Games", "public.app-category.family-games");
+        map.put("Kids Games", "public.app-category.kids-games");
+        map.put("Music Games", "public.app-category.music-games");
+        map.put("Puzzle Games", "public.app-category.puzzle-games");
+        map.put("Racing Games", "public.app-category.racing-games");
+        map.put("Role Playing Games", "public.app-category.role-playing-games");
+        map.put("Simulation Games", "public.app-category.simulation-games");
+        map.put("Sports Games", "public.app-category.sports-games");
+        map.put("Strategy Games", "public.app-category.strategy-games");
+        map.put("Trivia Games", "public.app-category.trivia-games");
+        map.put("Word Games", "public.app-category.word-games");
+
+        return map;
+    }
+
+    public static final EnumeratedBundlerParam<String> MAC_CATEGORY =
+            new EnumeratedBundlerParam<>(
+                    I18N.getString("param.category-name"),
+                    I18N.getString("param.category-name.description"),
+                    Arguments.CLIOptions.MAC_APP_STORE_CATEGORY.getId(),
+                    String.class,
+                    params -> params.containsKey(CATEGORY.getID())
+                            ? CATEGORY.fetchFrom(params)
+                            : "Unknown",
+                    (s, p) -> s,
+                    getMacCategories(),
+                    false //strict - for MacStoreBundler this should be strict
+            );
+
+    public static final BundlerParamInfo<String> MAC_CF_BUNDLE_NAME =
+            new StandardBundlerParam<>(
+                    I18N.getString("param.cfbundle-name.name"),
+                    I18N.getString("param.cfbundle-name.description"),
+                    Arguments.CLIOptions.MAC_BUNDLE_NAME.getId(),
+                    String.class,
+                    params -> null,
+                    (s, p) -> s);
+
+    public static final BundlerParamInfo<String> MAC_CF_BUNDLE_IDENTIFIER =
+            new StandardBundlerParam<>(
+                    I18N.getString("param.cfbundle-identifier.name"),
+                    I18N.getString("param.cfbundle-identifier.description"),
+                    Arguments.CLIOptions.MAC_BUNDLE_IDENTIFIER.getId(),
+                    String.class,
+                    IDENTIFIER::fetchFrom,
+                    (s, p) -> s);
+
+    public static final BundlerParamInfo<String> MAC_CF_BUNDLE_VERSION =
+            new StandardBundlerParam<>(
+                    I18N.getString("param.cfbundle-version.name"),
+                    I18N.getString("param.cfbundle-version.description"),
+                    "mac.CFBundleVersion",
+                    String.class,
+                    p -> {
+                        String s = VERSION.fetchFrom(p);
+                        if (validCFBundleVersion(s)) {
+                            return s;
+                        } else {
+                            return "100";
+                        }
+                    },
+                    (s, p) -> s);
+
+    public static final BundlerParamInfo<String> DEFAULT_ICNS_ICON =
+            new StandardBundlerParam<>(
+            I18N.getString("param.default-icon-icns"),
+            I18N.getString("param.default-icon-icns.description"),
+            ".mac.default.icns",
+            String.class,
+            params -> TEMPLATE_BUNDLE_ICON,
+            (s, p) -> s);
+
+    public static final BundlerParamInfo<String> DEVELOPER_ID_APP_SIGNING_KEY =
+            new StandardBundlerParam<>(
+            I18N.getString("param.signing-key-developer-id-app.name"),
+            I18N.getString("param.signing-key-developer-id-app.description"),
+            "mac.signing-key-developer-id-app",
+            String.class,
+            params -> {
+                    String result = MacBaseInstallerBundler.findKey(
+                            "Developer ID Application: "
+                            + SIGNING_KEY_USER.fetchFrom(params),
+                            SIGNING_KEYCHAIN.fetchFrom(params),
+                            VERBOSE.fetchFrom(params));
+                    if (result != null) {
+                        MacCertificate certificate = new MacCertificate(result,
+                                VERBOSE.fetchFrom(params));
+
+                        if (!certificate.isValid()) {
+                            Log.error(MessageFormat.format(I18N.getString(
+                                    "error.certificate.expired"), result));
+                        }
+                    }
+
+                    return result;
+                },
+            (s, p) -> s);
+
+    public static final BundlerParamInfo<String> BUNDLE_ID_SIGNING_PREFIX =
+            new StandardBundlerParam<>(
+            I18N.getString("param.bundle-id-signing-prefix.name"),
+            I18N.getString("param.bundle-id-signing-prefix.description"),
+            Arguments.CLIOptions.MAC_BUNDLE_SIGNING_PREFIX.getId(),
+            String.class,
+            params -> IDENTIFIER.fetchFrom(params) + ".",
+            (s, p) -> s);
+
+    public static final BundlerParamInfo<File> ICON_ICNS =
+            new StandardBundlerParam<>(
+            I18N.getString("param.icon-icns.name"),
+            I18N.getString("param.icon-icns.description"),
+            "icon.icns",
+            File.class,
+            params -> {
+                File f = ICON.fetchFrom(params);
+                if (f != null && !f.getName().toLowerCase().endsWith(".icns")) {
+                    Log.error(MessageFormat.format(
+                            I18N.getString("message.icon-not-icns"), f));
+                    return null;
+                }
+                return f;
+            },
+            (s, p) -> new File(s));
+
+    public static boolean validCFBundleVersion(String v) {
+        // CFBundleVersion (String - iOS, OS X) specifies the build version
+        // number of the bundle, which identifies an iteration (released or
+        // unreleased) of the bundle. The build version number should be a
+        // string comprised of three non-negative, period-separated integers
+        // with the first integer being greater than zero. The string should
+        // only contain numeric (0-9) and period (.) characters. Leading zeros
+        // are truncated from each integer and will be ignored (that is,
+        // 1.02.3 is equivalent to 1.2.3). This key is not localizable.
+
+        if (v == null) {
+            return false;
+        }
+
+        String p[] = v.split("\\.");
+        if (p.length > 3 || p.length < 1) {
+            Log.verbose(I18N.getString(
+                    "message.version-string-too-many-components"));
+            return false;
+        }
+
+        try {
+            BigInteger n = new BigInteger(p[0]);
+            if (BigInteger.ONE.compareTo(n) > 0) {
+                Log.verbose(I18N.getString(
+                        "message.version-string-first-number-not-zero"));
+                return false;
+            }
+            if (p.length > 1) {
+                n = new BigInteger(p[1]);
+                if (BigInteger.ZERO.compareTo(n) > 0) {
+                    Log.verbose(I18N.getString(
+                            "message.version-string-no-negative-numbers"));
+                    return false;
+                }
+            }
+            if (p.length > 2) {
+                n = new BigInteger(p[2]);
+                if (BigInteger.ZERO.compareTo(n) > 0) {
+                    Log.verbose(I18N.getString(
+                            "message.version-string-no-negative-numbers"));
+                    return false;
+                }
+            }
+        } catch (NumberFormatException ne) {
+            Log.verbose(I18N.getString("message.version-string-numbers-only"));
+            Log.verbose(ne);
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public boolean validate(Map<String, ? super Object> params)
+            throws UnsupportedPlatformException, ConfigException {
+        try {
+            return doValidate(params);
+        } catch (RuntimeException re) {
+            if (re.getCause() instanceof ConfigException) {
+                throw (ConfigException) re.getCause();
+            } else {
+                throw new ConfigException(re);
+            }
+        }
+    }
+
+    private boolean doValidate(Map<String, ? super Object> p)
+            throws UnsupportedPlatformException, ConfigException {
+        if (Platform.getPlatform() != Platform.MAC) {
+            throw new UnsupportedPlatformException();
+        }
+
+        imageBundleValidation(p);
+
+        if (StandardBundlerParam.getPredefinedAppImage(p) != null) {
+            return true;
+        }
+
+        // validate short version
+        if (!validCFBundleVersion(MAC_CF_BUNDLE_VERSION.fetchFrom(p))) {
+            throw new ConfigException(
+                    I18N.getString("error.invalid-cfbundle-version"),
+                    I18N.getString("error.invalid-cfbundle-version.advice"));
+        }
+
+        // reject explicitly set sign to true and no valid signature key
+        if (Optional.ofNullable(MacAppImageBuilder.
+                    SIGN_BUNDLE.fetchFrom(p)).orElse(Boolean.FALSE)) {
+            String signingIdentity = DEVELOPER_ID_APP_SIGNING_KEY.fetchFrom(p);
+            if (signingIdentity == null) {
+                throw new ConfigException(
+                        I18N.getString("error.explicit-sign-no-cert"),
+                        I18N.getString("error.explicit-sign-no-cert.advice"));
+            }
+        }
+
+        return true;
+    }
+
+    File doBundle(Map<String, ? super Object> p, File outputDirectory,
+            boolean dependentTask) throws PackagerException {
+        if (RUNTIME_INSTALLER.fetchFrom(p)) {
+            return doJreBundle(p, outputDirectory, dependentTask);
+        } else {
+            return doAppBundle(p, outputDirectory, dependentTask);
+        }
+    }
+
+    File doJreBundle(Map<String, ? super Object> p, File outputDirectory,
+            boolean dependentTask) throws PackagerException {
+        try {
+            File rootDirectory = createRoot(p, outputDirectory, dependentTask,
+                    APP_NAME.fetchFrom(p), "macapp-image-builder");
+            AbstractAppImageBuilder appBuilder = new MacAppImageBuilder(p,
+                    APP_NAME.fetchFrom(p), outputDirectory.toPath());
+            File predefined = PREDEFINED_RUNTIME_IMAGE.fetchFrom(p);
+            if (predefined == null ) {
+                JLinkBundlerHelper.generateJre(p, appBuilder);
+            } else {
+                return predefined;
+            }
+            return rootDirectory;
+        } catch (PackagerException pe) {
+            throw pe;
+        } catch (Exception ex) {
+            Log.verbose(ex);
+            throw new PackagerException(ex);
+        }
+    }
+
+    File doAppBundle(Map<String, ? super Object> p, File outputDirectory,
+            boolean dependentTask) throws PackagerException {
+        try {
+            File rootDirectory = createRoot(p, outputDirectory, dependentTask,
+                    APP_NAME.fetchFrom(p) + ".app", "macapp-image-builder");
+            AbstractAppImageBuilder appBuilder =
+                    new MacAppImageBuilder(p, outputDirectory.toPath());
+            if (PREDEFINED_RUNTIME_IMAGE.fetchFrom(p) == null ) {
+                JLinkBundlerHelper.execute(p, appBuilder);
+            } else {
+                StandardBundlerParam.copyPredefinedRuntimeImage(p, appBuilder);
+            }
+            return rootDirectory;
+        } catch (PackagerException pe) {
+            throw pe;
+        } catch (Exception ex) {
+            Log.verbose(ex);
+            throw new PackagerException(ex);
+        }
+    }
+
+    /////////////////////////////////////////////////////////////////////////
+    // Implement Bundler
+    /////////////////////////////////////////////////////////////////////////
+
+    @Override
+    public String getName() {
+        return I18N.getString("app.bundler.name");
+    }
+
+    @Override
+    public String getDescription() {
+        return I18N.getString("app.bundler.description");
+    }
+
+    @Override
+    public String getID() {
+        return "mac.app";
+    }
+
+    @Override
+    public String getBundleType() {
+        return "IMAGE";
+    }
+
+    @Override
+    public Collection<BundlerParamInfo<?>> getBundleParameters() {
+        return getAppBundleParameters();
+    }
+
+    public static Collection<BundlerParamInfo<?>> getAppBundleParameters() {
+        return Arrays.asList(
+                APP_NAME,
+                APP_RESOURCES,
+                ARGUMENTS,
+                BUNDLE_ID_SIGNING_PREFIX,
+                CLASSPATH,
+                DEVELOPER_ID_APP_SIGNING_KEY,
+                ICON_ICNS,
+                JVM_OPTIONS,
+                MAC_CATEGORY,
+                MAC_CF_BUNDLE_IDENTIFIER,
+                MAC_CF_BUNDLE_NAME,
+                MAC_CF_BUNDLE_VERSION,
+                MAIN_CLASS,
+                MAIN_JAR,
+                PREFERENCES_ID,
+                SIGNING_KEYCHAIN,
+                VERSION,
+                VERBOSE
+        );
+    }
+
+
+    @Override
+    public File execute(Map<String, ? super Object> params,
+            File outputParentDir) throws PackagerException {
+        return doBundle(params, outputParentDir, false);
+    }
+
+    @Override
+    public boolean supported(boolean runtimeInstaller) {
+        return Platform.getPlatform() == Platform.MAC;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacAppImageBuilder.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,975 @@
+/*
+ * Copyright (c) 2015, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jpackage.internal;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.UncheckedIOException;
+import java.io.Writer;
+import java.math.BigInteger;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+import java.nio.file.attribute.PosixFilePermission;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.ResourceBundle;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
+
+import static jdk.jpackage.internal.StandardBundlerParam.*;
+import static jdk.jpackage.internal.MacBaseInstallerBundler.*;
+import static jdk.jpackage.internal.MacAppBundler.*;
+
+public class MacAppImageBuilder extends AbstractAppImageBuilder {
+
+    private static final ResourceBundle I18N = ResourceBundle.getBundle(
+            "jdk.jpackage.internal.resources.MacResources");
+
+    private static final String LIBRARY_NAME = "libapplauncher.dylib";
+    private static final String TEMPLATE_BUNDLE_ICON = "GenericApp.icns";
+    private static final String OS_TYPE_CODE = "APPL";
+    private static final String TEMPLATE_INFO_PLIST_LITE =
+            "Info-lite.plist.template";
+    private static final String TEMPLATE_RUNTIME_INFO_PLIST =
+            "Runtime-Info.plist.template";
+
+    private final Path root;
+    private final Path contentsDir;
+    private final Path javaDir;
+    private final Path javaModsDir;
+    private final Path resourcesDir;
+    private final Path macOSDir;
+    private final Path runtimeDir;
+    private final Path runtimeRoot;
+    private final Path mdir;
+
+    private final Map<String, ? super Object> params;
+
+    private static List<String> keyChains;
+
+    public static final BundlerParamInfo<Boolean>
+            MAC_CONFIGURE_LAUNCHER_IN_PLIST = new StandardBundlerParam<>(
+                    I18N.getString("param.configure-launcher-in-plist"),
+                    I18N.getString(
+                            "param.configure-launcher-in-plist.description"),
+                    "mac.configure-launcher-in-plist",
+                    Boolean.class,
+                    params -> Boolean.FALSE,
+                    (s, p) -> Boolean.valueOf(s));
+
+    public static final BundlerParamInfo<String> MAC_CATEGORY =
+            new StandardBundlerParam<>(
+                    I18N.getString("param.category-name"),
+                    I18N.getString("param.category-name.description"),
+                    "mac.category",
+                    String.class,
+                    CATEGORY::fetchFrom,
+                    (s, p) -> s
+            );
+
+    public static final BundlerParamInfo<String> MAC_CF_BUNDLE_NAME =
+            new StandardBundlerParam<>(
+                    I18N.getString("param.cfbundle-name.name"),
+                    I18N.getString("param.cfbundle-name.description"),
+                    "mac.CFBundleName",
+                    String.class,
+                    params -> null,
+                    (s, p) -> s);
+
+    public static final BundlerParamInfo<String> MAC_CF_BUNDLE_IDENTIFIER =
+            new StandardBundlerParam<>(
+                    I18N.getString("param.cfbundle-identifier.name"),
+                    I18N.getString("param.cfbundle-identifier.description"),
+                    Arguments.CLIOptions.MAC_BUNDLE_IDENTIFIER.getId(),
+                    String.class,
+                    IDENTIFIER::fetchFrom,
+                    (s, p) -> s);
+
+    public static final BundlerParamInfo<String> MAC_CF_BUNDLE_VERSION =
+            new StandardBundlerParam<>(
+                    I18N.getString("param.cfbundle-version.name"),
+                    I18N.getString("param.cfbundle-version.description"),
+                    "mac.CFBundleVersion",
+                    String.class,
+                    p -> {
+                        String s = VERSION.fetchFrom(p);
+                        if (validCFBundleVersion(s)) {
+                            return s;
+                        } else {
+                            return "100";
+                        }
+                    },
+                    (s, p) -> s);
+
+    public static final BundlerParamInfo<String> DEFAULT_ICNS_ICON =
+            new StandardBundlerParam<>(
+            I18N.getString("param.default-icon-icns"),
+            I18N.getString("param.default-icon-icns.description"),
+            ".mac.default.icns",
+            String.class,
+            params -> TEMPLATE_BUNDLE_ICON,
+            (s, p) -> s);
+
+    public static final BundlerParamInfo<File> ICON_ICNS =
+            new StandardBundlerParam<>(
+            I18N.getString("param.icon-icns.name"),
+            I18N.getString("param.icon-icns.description"),
+            "icon.icns",
+            File.class,
+            params -> {
+                File f = ICON.fetchFrom(params);
+                if (f != null && !f.getName().toLowerCase().endsWith(".icns")) {
+                    Log.error(MessageFormat.format(
+                            I18N.getString("message.icon-not-icns"), f));
+                    return null;
+                }
+                return f;
+            },
+            (s, p) -> new File(s));
+
+    public static final StandardBundlerParam<Boolean> SIGN_BUNDLE  =
+            new StandardBundlerParam<>(
+            I18N.getString("param.sign-bundle.name"),
+            I18N.getString("param.sign-bundle.description"),
+            Arguments.CLIOptions.MAC_SIGN.getId(),
+            Boolean.class,
+            params -> false,
+            // valueOf(null) is false, we actually do want null in some cases
+            (s, p) -> (s == null || "null".equalsIgnoreCase(s)) ?
+                    null : Boolean.valueOf(s)
+        );
+
+    public MacAppImageBuilder(Map<String, Object> config, Path imageOutDir)
+            throws IOException {
+        super(config, imageOutDir.resolve(APP_NAME.fetchFrom(config)
+                + ".app/Contents/PlugIns/Java.runtime/Contents/Home"));
+
+        Objects.requireNonNull(imageOutDir);
+
+        this.params = config;
+        this.root = imageOutDir.resolve(APP_NAME.fetchFrom(params) + ".app");
+        this.contentsDir = root.resolve("Contents");
+        this.javaDir = contentsDir.resolve("Java");
+        this.javaModsDir = javaDir.resolve("mods");
+        this.resourcesDir = contentsDir.resolve("Resources");
+        this.macOSDir = contentsDir.resolve("MacOS");
+        this.runtimeDir = contentsDir.resolve("PlugIns/Java.runtime");
+        this.runtimeRoot = runtimeDir.resolve("Contents/Home");
+        this.mdir = runtimeRoot.resolve("lib");
+        Files.createDirectories(javaDir);
+        Files.createDirectories(resourcesDir);
+        Files.createDirectories(macOSDir);
+        Files.createDirectories(runtimeDir);
+    }
+
+    public MacAppImageBuilder(Map<String, Object> config, String jreName,
+            Path imageOutDir) throws IOException {
+        super(null, imageOutDir.resolve(jreName + "/Contents/Home"));
+
+        Objects.requireNonNull(imageOutDir);
+
+        this.params = config;
+        this.root = imageOutDir.resolve(jreName );
+        this.contentsDir = root.resolve("Contents");
+        this.javaDir = null;
+        this.javaModsDir = null;
+        this.resourcesDir = null;
+        this.macOSDir = null;
+        this.runtimeDir = this.root;
+        this.runtimeRoot = runtimeDir.resolve("Contents/Home");
+        this.mdir = runtimeRoot.resolve("lib");
+
+        Files.createDirectories(runtimeDir);
+    }
+
+    private void writeEntry(InputStream in, Path dstFile) throws IOException {
+        Files.createDirectories(dstFile.getParent());
+        Files.copy(in, dstFile);
+    }
+
+    // chmod ugo+x file
+    private void setExecutable(Path file) {
+        try {
+            Set<PosixFilePermission> perms =
+                    Files.getPosixFilePermissions(file);
+            perms.add(PosixFilePermission.OWNER_EXECUTE);
+            perms.add(PosixFilePermission.GROUP_EXECUTE);
+            perms.add(PosixFilePermission.OTHERS_EXECUTE);
+            Files.setPosixFilePermissions(file, perms);
+        } catch (IOException ioe) {
+            throw new UncheckedIOException(ioe);
+        }
+    }
+
+    private static void createUtf8File(File file, String content)
+        throws IOException {
+        try (OutputStream fout = new FileOutputStream(file);
+             Writer output = new OutputStreamWriter(fout, "UTF-8")) {
+            output.write(content);
+        }
+    }
+
+    public static boolean validCFBundleVersion(String v) {
+        // CFBundleVersion (String - iOS, OS X) specifies the build version
+        // number of the bundle, which identifies an iteration (released or
+        // unreleased) of the bundle. The build version number should be a
+        // string comprised of three non-negative, period-separated integers
+        // with the first integer being greater than zero. The string should
+        // only contain numeric (0-9) and period (.) characters. Leading zeros
+        // are truncated from each integer and will be ignored (that is,
+        // 1.02.3 is equivalent to 1.2.3). This key is not localizable.
+
+        if (v == null) {
+            return false;
+        }
+
+        String p[] = v.split("\\.");
+        if (p.length > 3 || p.length < 1) {
+            Log.verbose(I18N.getString(
+                    "message.version-string-too-many-components"));
+            return false;
+        }
+
+        try {
+            BigInteger n = new BigInteger(p[0]);
+            if (BigInteger.ONE.compareTo(n) > 0) {
+                Log.verbose(I18N.getString(
+                        "message.version-string-first-number-not-zero"));
+                return false;
+            }
+            if (p.length > 1) {
+                n = new BigInteger(p[1]);
+                if (BigInteger.ZERO.compareTo(n) > 0) {
+                    Log.verbose(I18N.getString(
+                            "message.version-string-no-negative-numbers"));
+                    return false;
+                }
+            }
+            if (p.length > 2) {
+                n = new BigInteger(p[2]);
+                if (BigInteger.ZERO.compareTo(n) > 0) {
+                    Log.verbose(I18N.getString(
+                            "message.version-string-no-negative-numbers"));
+                    return false;
+                }
+            }
+        } catch (NumberFormatException ne) {
+            Log.verbose(I18N.getString("message.version-string-numbers-only"));
+            Log.verbose(ne);
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public Path getAppDir() {
+        return javaDir;
+    }
+
+    @Override
+    public Path getAppModsDir() {
+        return javaModsDir;
+    }
+
+    @Override
+    public void prepareApplicationFiles() throws IOException {
+        Map<String, ? super Object> originalParams = new HashMap<>(params);
+        // Generate PkgInfo
+        File pkgInfoFile = new File(contentsDir.toFile(), "PkgInfo");
+        pkgInfoFile.createNewFile();
+        writePkgInfo(pkgInfoFile);
+
+        Path executable = macOSDir.resolve(getLauncherName(params));
+
+        // create the main app launcher
+        try (InputStream is_launcher =
+                getResourceAsStream("jpackageapplauncher");
+            InputStream is_lib = getResourceAsStream(LIBRARY_NAME)) {
+            // Copy executable and library to MacOS folder
+            writeEntry(is_launcher, executable);
+            writeEntry(is_lib, macOSDir.resolve(LIBRARY_NAME));
+        }
+        executable.toFile().setExecutable(true, false);
+        // generate main app launcher config file
+        File cfg = new File(root.toFile(), getLauncherCfgName(params));
+        writeCfgFile(params, cfg, "$APPDIR/PlugIns/Java.runtime");
+
+        // create secondary app launcher(s) and config file(s)
+        List<Map<String, ? super Object>> entryPoints =
+                StandardBundlerParam.SECONDARY_LAUNCHERS.fetchFrom(params);
+        for (Map<String, ? super Object> entryPoint : entryPoints) {
+            Map<String, ? super Object> tmp = new HashMap<>(originalParams);
+            tmp.putAll(entryPoint);
+
+            // add executable for secondary launcher
+            Path secondaryExecutable = macOSDir.resolve(getLauncherName(tmp));
+            try (InputStream is = getResourceAsStream("jpackageapplauncher");) {
+                writeEntry(is, secondaryExecutable);
+            }
+            secondaryExecutable.toFile().setExecutable(true, false);
+
+            // add config file for secondary launcher
+            cfg = new File(root.toFile(), getLauncherCfgName(tmp));
+            writeCfgFile(tmp, cfg, "$APPDIR/PlugIns/Java.runtime");
+        }
+
+        // Copy class path entries to Java folder
+        copyClassPathEntries(javaDir);
+
+        /*********** Take care of "config" files *******/
+        File icon = ICON_ICNS.fetchFrom(params);
+
+        InputStream in = locateResource(
+                APP_NAME.fetchFrom(params) + ".icns",
+                "icon",
+                DEFAULT_ICNS_ICON.fetchFrom(params),
+                icon,
+                VERBOSE.fetchFrom(params),
+                RESOURCE_DIR.fetchFrom(params));
+        Files.copy(in,
+                resourcesDir.resolve(APP_NAME.fetchFrom(params) + ".icns"),
+                StandardCopyOption.REPLACE_EXISTING);
+
+        // copy file association icons
+        for (Map<String, ?
+                super Object> fa : FILE_ASSOCIATIONS.fetchFrom(params)) {
+            File f = FA_ICON.fetchFrom(fa);
+            if (f != null && f.exists()) {
+                try (InputStream in2 = new FileInputStream(f)) {
+                    Files.copy(in2, resourcesDir.resolve(f.getName()));
+                }
+
+            }
+        }
+
+        copyRuntimeFiles();
+        sign();
+    }
+
+    @Override
+    public void prepareJreFiles() throws IOException {
+        copyRuntimeFiles();
+        sign();
+    }
+
+    private void copyRuntimeFiles() throws IOException {
+        // Generate Info.plist
+        writeInfoPlist(contentsDir.resolve("Info.plist").toFile());
+
+        // generate java runtime info.plist
+        writeRuntimeInfoPlist(
+                runtimeDir.resolve("Contents/Info.plist").toFile());
+
+        // copy library
+        Path runtimeMacOSDir = Files.createDirectories(
+                runtimeDir.resolve("Contents/MacOS"));
+
+        // JDK 9, 10, and 11 have extra '/jli/' subdir
+        Path jli = runtimeRoot.resolve("lib/libjli.dylib");
+        if (!Files.exists(jli)) {
+            jli = runtimeRoot.resolve("lib/jli/libjli.dylib");
+        }
+
+        Files.copy(jli, runtimeMacOSDir.resolve("libjli.dylib"));
+    }
+
+    private void sign() throws IOException {
+        if (Optional.ofNullable(
+                SIGN_BUNDLE.fetchFrom(params)).orElse(Boolean.TRUE)) {
+            try {
+                addNewKeychain(params);
+            } catch (InterruptedException e) {
+                Log.error(e.getMessage());
+            }
+            String signingIdentity =
+                    DEVELOPER_ID_APP_SIGNING_KEY.fetchFrom(params);
+            if (signingIdentity != null) {
+                signAppBundle(params, root, signingIdentity,
+                        BUNDLE_ID_SIGNING_PREFIX.fetchFrom(params), null, null);
+            }
+            restoreKeychainList(params);
+        }
+    }
+
+    private String getLauncherName(Map<String, ? super Object> params) {
+        if (APP_NAME.fetchFrom(params) != null) {
+            return APP_NAME.fetchFrom(params);
+        } else {
+            return MAIN_CLASS.fetchFrom(params);
+        }
+    }
+
+    public static String getLauncherCfgName(Map<String, ? super Object> p) {
+        return "Contents/Java/" + APP_NAME.fetchFrom(p) + ".cfg";
+    }
+
+    private void copyClassPathEntries(Path javaDirectory) throws IOException {
+        List<RelativeFileSet> resourcesList =
+                APP_RESOURCES_LIST.fetchFrom(params);
+        if (resourcesList == null) {
+            throw new RuntimeException(
+                    I18N.getString("message.null-classpath"));
+        }
+
+        for (RelativeFileSet classPath : resourcesList) {
+            File srcdir = classPath.getBaseDirectory();
+            for (String fname : classPath.getIncludedFiles()) {
+                copyEntry(javaDirectory, srcdir, fname);
+            }
+        }
+    }
+
+    private String getBundleName(Map<String, ? super Object> params) {
+        if (MAC_CF_BUNDLE_NAME.fetchFrom(params) != null) {
+            String bn = MAC_CF_BUNDLE_NAME.fetchFrom(params);
+            if (bn.length() > 16) {
+                Log.error(MessageFormat.format(I18N.getString(
+                        "message.bundle-name-too-long-warning"),
+                        MAC_CF_BUNDLE_NAME.getID(), bn));
+            }
+            return MAC_CF_BUNDLE_NAME.fetchFrom(params);
+        } else if (APP_NAME.fetchFrom(params) != null) {
+            return APP_NAME.fetchFrom(params);
+        } else {
+            String nm = MAIN_CLASS.fetchFrom(params);
+            if (nm.length() > 16) {
+                nm = nm.substring(0, 16);
+            }
+            return nm;
+        }
+    }
+
+    private void writeRuntimeInfoPlist(File file) throws IOException {
+        Map<String, String> data = new HashMap<>();
+        String identifier = RUNTIME_INSTALLER.fetchFrom(params) ?
+                MAC_CF_BUNDLE_IDENTIFIER.fetchFrom(params) :
+                "com.oracle.java." + MAC_CF_BUNDLE_IDENTIFIER.fetchFrom(params);
+        data.put("CF_BUNDLE_IDENTIFIER", identifier);
+        String name = RUNTIME_INSTALLER.fetchFrom(params) ?
+                getBundleName(params): "Java Runtime Image";
+        data.put("CF_BUNDLE_NAME", name);
+        data.put("CF_BUNDLE_VERSION", VERSION.fetchFrom(params));
+        data.put("CF_BUNDLE_SHORT_VERSION_STRING", VERSION.fetchFrom(params));
+
+        Writer w = new BufferedWriter(new FileWriter(file));
+        w.write(preprocessTextResource("Runtime-Info.plist",
+                I18N.getString("resource.runtime-info-plist"),
+                TEMPLATE_RUNTIME_INFO_PLIST,
+                data,
+                VERBOSE.fetchFrom(params),
+                RESOURCE_DIR.fetchFrom(params)));
+        w.close();
+    }
+
+    private void writeInfoPlist(File file) throws IOException {
+        Log.verbose(MessageFormat.format(I18N.getString(
+                "message.preparing-info-plist"), file.getAbsolutePath()));
+
+        //prepare config for exe
+        //Note: do not need CFBundleDisplayName if we don't support localization
+        Map<String, String> data = new HashMap<>();
+        data.put("DEPLOY_ICON_FILE", APP_NAME.fetchFrom(params) + ".icns");
+        data.put("DEPLOY_BUNDLE_IDENTIFIER",
+                MAC_CF_BUNDLE_IDENTIFIER.fetchFrom(params));
+        data.put("DEPLOY_BUNDLE_NAME",
+                getBundleName(params));
+        data.put("DEPLOY_BUNDLE_COPYRIGHT",
+                COPYRIGHT.fetchFrom(params) != null ?
+                COPYRIGHT.fetchFrom(params) : "Unknown");
+        data.put("DEPLOY_LAUNCHER_NAME", getLauncherName(params));
+        data.put("DEPLOY_JAVA_RUNTIME_NAME", "$APPDIR/PlugIns/Java.runtime");
+        data.put("DEPLOY_BUNDLE_SHORT_VERSION",
+                VERSION.fetchFrom(params) != null ?
+                VERSION.fetchFrom(params) : "1.0.0");
+        data.put("DEPLOY_BUNDLE_CFBUNDLE_VERSION",
+                MAC_CF_BUNDLE_VERSION.fetchFrom(params) != null ?
+                MAC_CF_BUNDLE_VERSION.fetchFrom(params) : "100");
+        data.put("DEPLOY_BUNDLE_CATEGORY", MAC_CATEGORY.fetchFrom(params));
+
+        boolean hasMainJar = MAIN_JAR.fetchFrom(params) != null;
+        boolean hasMainModule =
+                StandardBundlerParam.MODULE.fetchFrom(params) != null;
+
+        if (hasMainJar) {
+            data.put("DEPLOY_MAIN_JAR_NAME", MAIN_JAR.fetchFrom(params).
+                    getIncludedFiles().iterator().next());
+        }
+        else if (hasMainModule) {
+            data.put("DEPLOY_MODULE_NAME",
+                    StandardBundlerParam.MODULE.fetchFrom(params));
+        }
+
+        data.put("DEPLOY_PREFERENCES_ID",
+                PREFERENCES_ID.fetchFrom(params).toLowerCase());
+
+        StringBuilder sb = new StringBuilder();
+        List<String> jvmOptions = JVM_OPTIONS.fetchFrom(params);
+
+        String newline = ""; //So we don't add extra line after last append
+        for (String o : jvmOptions) {
+            sb.append(newline).append(
+                    "    <string>").append(o).append("</string>");
+            newline = "\n";
+        }
+
+        data.put("DEPLOY_JVM_OPTIONS", sb.toString());
+
+        sb = new StringBuilder();
+        List<String> args = ARGUMENTS.fetchFrom(params);
+        newline = "";
+        // So we don't add unneccessary extra line after last append
+
+        for (String o : args) {
+            sb.append(newline).append("    <string>").append(o).append(
+                    "</string>");
+            newline = "\n";
+        }
+        data.put("DEPLOY_ARGUMENTS", sb.toString());
+
+        newline = "";
+
+        data.put("DEPLOY_LAUNCHER_CLASS", MAIN_CLASS.fetchFrom(params));
+
+        StringBuilder macroedPath = new StringBuilder();
+        for (String s : CLASSPATH.fetchFrom(params).split("[ ;:]+")) {
+            macroedPath.append(s);
+            macroedPath.append(":");
+        }
+        macroedPath.deleteCharAt(macroedPath.length() - 1);
+
+        data.put("DEPLOY_APP_CLASSPATH", macroedPath.toString());
+
+        StringBuilder bundleDocumentTypes = new StringBuilder();
+        StringBuilder exportedTypes = new StringBuilder();
+        for (Map<String, ? super Object>
+                fileAssociation : FILE_ASSOCIATIONS.fetchFrom(params)) {
+
+            List<String> extensions = FA_EXTENSIONS.fetchFrom(fileAssociation);
+
+            if (extensions == null) {
+                Log.verbose(I18N.getString(
+                        "message.creating-association-with-null-extension"));
+            }
+
+            List<String> mimeTypes = FA_CONTENT_TYPE.fetchFrom(fileAssociation);
+            String itemContentType = MAC_CF_BUNDLE_IDENTIFIER.fetchFrom(params)
+                    + "." + ((extensions == null || extensions.isEmpty())
+                    ? "mime" : extensions.get(0));
+            String description = FA_DESCRIPTION.fetchFrom(fileAssociation);
+            File icon = FA_ICON.fetchFrom(fileAssociation); //TODO FA_ICON_ICNS
+
+            bundleDocumentTypes.append("    <dict>\n")
+                    .append("      <key>LSItemContentTypes</key>\n")
+                    .append("      <array>\n")
+                    .append("        <string>")
+                    .append(itemContentType)
+                    .append("</string>\n")
+                    .append("      </array>\n")
+                    .append("\n")
+                    .append("      <key>CFBundleTypeName</key>\n")
+                    .append("      <string>")
+                    .append(description)
+                    .append("</string>\n")
+                    .append("\n")
+                    .append("      <key>LSHandlerRank</key>\n")
+                    .append("      <string>Owner</string>\n")
+                            // TODO make a bundler arg
+                    .append("\n")
+                    .append("      <key>CFBundleTypeRole</key>\n")
+                    .append("      <string>Editor</string>\n")
+                            // TODO make a bundler arg
+                    .append("\n")
+                    .append("      <key>LSIsAppleDefaultForType</key>\n")
+                    .append("      <true/>\n")
+                            // TODO make a bundler arg
+                    .append("\n");
+
+            if (icon != null && icon.exists()) {
+                bundleDocumentTypes
+                        .append("      <key>CFBundleTypeIconFile</key>\n")
+                        .append("      <string>")
+                        .append(icon.getName())
+                        .append("</string>\n");
+            }
+            bundleDocumentTypes.append("    </dict>\n");
+
+            exportedTypes.append("    <dict>\n")
+                    .append("      <key>UTTypeIdentifier</key>\n")
+                    .append("      <string>")
+                    .append(itemContentType)
+                    .append("</string>\n")
+                    .append("\n")
+                    .append("      <key>UTTypeDescription</key>\n")
+                    .append("      <string>")
+                    .append(description)
+                    .append("</string>\n")
+                    .append("      <key>UTTypeConformsTo</key>\n")
+                    .append("      <array>\n")
+                    .append("          <string>public.data</string>\n")
+                            //TODO expose this?
+                    .append("      </array>\n")
+                    .append("\n");
+
+            if (icon != null && icon.exists()) {
+                exportedTypes.append("      <key>UTTypeIconFile</key>\n")
+                        .append("      <string>")
+                        .append(icon.getName())
+                        .append("</string>\n")
+                        .append("\n");
+            }
+
+            exportedTypes.append("\n")
+                    .append("      <key>UTTypeTagSpecification</key>\n")
+                    .append("      <dict>\n")
+                            // TODO expose via param? .append(
+                            // "        <key>com.apple.ostype</key>\n");
+                            // TODO expose via param? .append(
+                            // "        <string>ABCD</string>\n")
+                    .append("\n");
+
+            if (extensions != null && !extensions.isEmpty()) {
+                exportedTypes.append(
+                        "        <key>public.filename-extension</key>\n")
+                        .append("        <array>\n");
+
+                for (String ext : extensions) {
+                    exportedTypes.append("          <string>")
+                            .append(ext)
+                            .append("</string>\n");
+                }
+                exportedTypes.append("        </array>\n");
+            }
+            if (mimeTypes != null && !mimeTypes.isEmpty()) {
+                exportedTypes.append("        <key>public.mime-type</key>\n")
+                        .append("        <array>\n");
+
+                for (String mime : mimeTypes) {
+                    exportedTypes.append("          <string>")
+                            .append(mime)
+                            .append("</string>\n");
+                }
+                exportedTypes.append("        </array>\n");
+            }
+            exportedTypes.append("      </dict>\n")
+                    .append("    </dict>\n");
+        }
+        String associationData;
+        if (bundleDocumentTypes.length() > 0) {
+            associationData =
+                    "\n  <key>CFBundleDocumentTypes</key>\n  <array>\n"
+                    + bundleDocumentTypes.toString()
+                    + "  </array>\n\n"
+                    + "  <key>UTExportedTypeDeclarations</key>\n  <array>\n"
+                    + exportedTypes.toString()
+                    + "  </array>\n";
+        } else {
+            associationData = "";
+        }
+        data.put("DEPLOY_FILE_ASSOCIATIONS", associationData);
+
+
+        Writer w = new BufferedWriter(new FileWriter(file));
+        w.write(preprocessTextResource(
+                // getConfig_InfoPlist(params).getName(),
+                "Info.plist",
+                I18N.getString("resource.app-info-plist"),
+                TEMPLATE_INFO_PLIST_LITE,
+                data, VERBOSE.fetchFrom(params),
+                RESOURCE_DIR.fetchFrom(params)));
+        w.close();
+    }
+
+    private void writePkgInfo(File file) throws IOException {
+        //hardcoded as it does not seem we need to change it ever
+        String signature = "????";
+
+        try (Writer out = new BufferedWriter(new FileWriter(file))) {
+            out.write(OS_TYPE_CODE + signature);
+            out.flush();
+        }
+    }
+
+    public static void addNewKeychain(Map<String, ? super Object> params)
+                                    throws IOException, InterruptedException {
+        if (Platform.getMajorVersion() < 10 ||
+                (Platform.getMajorVersion() == 10 &&
+                Platform.getMinorVersion() < 12)) {
+            // we need this for OS X 10.12+
+            return;
+        }
+
+        String keyChain = SIGNING_KEYCHAIN.fetchFrom(params);
+        if (keyChain == null || keyChain.isEmpty()) {
+            return;
+        }
+
+        // get current keychain list
+        String keyChainPath = new File (keyChain).getAbsolutePath().toString();
+        List<String> keychainList = new ArrayList<>();
+        int ret = IOUtils.getProcessOutput(
+                keychainList, "security", "list-keychains");
+        if (ret != 0) {
+            Log.error(I18N.getString("message.keychain.error"));
+            return;
+        }
+
+        boolean contains = keychainList.stream().anyMatch(
+                    str -> str.trim().equals("\""+keyChainPath.trim()+"\""));
+        if (contains) {
+            // keychain is already added in the search list
+            return;
+        }
+
+        keyChains = new ArrayList<>();
+        // remove "
+        keychainList.forEach((String s) -> {
+            String path = s.trim();
+            if (path.startsWith("\"") && path.endsWith("\"")) {
+                path = path.substring(1, path.length()-1);
+            }
+            keyChains.add(path);
+        });
+
+        List<String> args = new ArrayList<>();
+        args.add("security");
+        args.add("list-keychains");
+        args.add("-s");
+
+        args.addAll(keyChains);
+        args.add(keyChain);
+
+        ProcessBuilder  pb = new ProcessBuilder(args);
+        IOUtils.exec(pb, false);
+    }
+
+    public static void restoreKeychainList(Map<String, ? super Object> params)
+            throws IOException{
+        if (Platform.getMajorVersion() < 10 ||
+                (Platform.getMajorVersion() == 10 &&
+                Platform.getMinorVersion() < 12)) {
+            // we need this for OS X 10.12+
+            return;
+        }
+
+        if (keyChains == null || keyChains.isEmpty()) {
+            return;
+        }
+
+        List<String> args = new ArrayList<>();
+        args.add("security");
+        args.add("list-keychains");
+        args.add("-s");
+
+        args.addAll(keyChains);
+
+        ProcessBuilder  pb = new ProcessBuilder(args);
+        IOUtils.exec(pb, false);
+    }
+
+    public static void signAppBundle(
+            Map<String, ? super Object> params, Path appLocation,
+            String signingIdentity, String identifierPrefix,
+            String entitlementsFile, String inheritedEntitlements)
+            throws IOException {
+        AtomicReference<IOException> toThrow = new AtomicReference<>();
+        String appExecutable = "/Contents/MacOS/" + APP_NAME.fetchFrom(params);
+        String keyChain = SIGNING_KEYCHAIN.fetchFrom(params);
+
+        // sign all dylibs and jars
+        Files.walk(appLocation)
+                // fix permissions
+                .peek(path -> {
+                    try {
+                        Set<PosixFilePermission> pfp =
+                            Files.getPosixFilePermissions(path);
+                        if (!pfp.contains(PosixFilePermission.OWNER_WRITE)) {
+                            pfp = EnumSet.copyOf(pfp);
+                            pfp.add(PosixFilePermission.OWNER_WRITE);
+                            Files.setPosixFilePermissions(path, pfp);
+                        }
+                    } catch (IOException e) {
+                        Log.debug(e);
+                    }
+                })
+                .filter(p -> Files.isRegularFile(p) &&
+                        !(p.toString().contains("/Contents/MacOS/libjli.dylib")
+                        || p.toString().contains(
+                                "/Contents/MacOS/JavaAppletPlugin")
+                        || p.toString().endsWith(appExecutable))
+                ).forEach(p -> {
+            //noinspection ThrowableResultOfMethodCallIgnored
+            if (toThrow.get() != null) return;
+
+            // If p is a symlink then skip the signing process.
+            if (Files.isSymbolicLink(p)) {
+                if (VERBOSE.fetchFrom(params)) {
+                    Log.verbose(MessageFormat.format(I18N.getString(
+                            "message.ignoring.symlink"), p.toString()));
+                }
+            }
+            else {
+                List<String> args = new ArrayList<>();
+                args.addAll(Arrays.asList("codesign",
+                        "-s", signingIdentity, // sign with this key
+                        "--prefix", identifierPrefix,
+                                // use the identifier as a prefix
+                        "-vvvv"));
+                if (entitlementsFile != null &&
+                        (p.toString().endsWith(".jar")
+                                || p.toString().endsWith(".dylib"))) {
+                    args.add("--entitlements");
+                    args.add(entitlementsFile); // entitlements
+                } else if (inheritedEntitlements != null &&
+                        Files.isExecutable(p)) {
+                    args.add("--entitlements");
+                    args.add(inheritedEntitlements);
+                            // inherited entitlements for executable processes
+                }
+                if (keyChain != null && !keyChain.isEmpty()) {
+                    args.add("--keychain");
+                    args.add(keyChain);
+                }
+                args.add(p.toString());
+
+                try {
+                    Set<PosixFilePermission> oldPermissions =
+                            Files.getPosixFilePermissions(p);
+                    File f = p.toFile();
+                    f.setWritable(true, true);
+
+                    ProcessBuilder pb = new ProcessBuilder(args);
+                    IOUtils.exec(pb, false);
+
+                    Files.setPosixFilePermissions(p, oldPermissions);
+                } catch (IOException ioe) {
+                    toThrow.set(ioe);
+                }
+            }
+        });
+
+        IOException ioe = toThrow.get();
+        if (ioe != null) {
+            throw ioe;
+        }
+
+        // sign all plugins and frameworks
+        Consumer<? super Path> signIdentifiedByPList = path -> {
+            //noinspection ThrowableResultOfMethodCallIgnored
+            if (toThrow.get() != null) return;
+
+            try {
+                List<String> args = new ArrayList<>();
+                args.addAll(Arrays.asList("codesign",
+                        "-s", signingIdentity, // sign with this key
+                        "--prefix", identifierPrefix,
+                                // use the identifier as a prefix
+                        "-vvvv"));
+                if (keyChain != null && !keyChain.isEmpty()) {
+                    args.add("--keychain");
+                    args.add(keyChain);
+                }
+                args.add(path.toString());
+                ProcessBuilder pb = new ProcessBuilder(args);
+                IOUtils.exec(pb, false);
+
+                args = new ArrayList<>();
+                args.addAll(Arrays.asList("codesign",
+                        "-s", signingIdentity, // sign with this key
+                        "--prefix", identifierPrefix,
+                                // use the identifier as a prefix
+                        "-vvvv"));
+                if (keyChain != null && !keyChain.isEmpty()) {
+                    args.add("--keychain");
+                    args.add(keyChain);
+                }
+                args.add(path.toString()
+                        + "/Contents/_CodeSignature/CodeResources");
+                pb = new ProcessBuilder(args);
+                IOUtils.exec(pb, false);
+            } catch (IOException e) {
+                toThrow.set(e);
+            }
+        };
+
+        Path pluginsPath = appLocation.resolve("Contents/PlugIns");
+        if (Files.isDirectory(pluginsPath)) {
+            Files.list(pluginsPath)
+                    .forEach(signIdentifiedByPList);
+
+            ioe = toThrow.get();
+            if (ioe != null) {
+                throw ioe;
+            }
+        }
+        Path frameworkPath = appLocation.resolve("Contents/Frameworks");
+        if (Files.isDirectory(frameworkPath)) {
+            Files.list(frameworkPath)
+                    .forEach(signIdentifiedByPList);
+
+            ioe = toThrow.get();
+            if (ioe != null) {
+                throw ioe;
+            }
+        }
+
+        // sign the app itself
+        List<String> args = new ArrayList<>();
+        args.addAll(Arrays.asList("codesign",
+                "-s", signingIdentity, // sign with this key
+                "-vvvv")); // super verbose output
+        if (entitlementsFile != null) {
+            args.add("--entitlements");
+            args.add(entitlementsFile); // entitlements
+        }
+        if (keyChain != null && !keyChain.isEmpty()) {
+            args.add("--keychain");
+            args.add(keyChain);
+        }
+        args.add(appLocation.toString());
+
+        ProcessBuilder pb =
+                new ProcessBuilder(args.toArray(new String[args.size()]));
+        IOUtils.exec(pb, false);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacAppStoreBundler.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,375 @@
+/*
+ * Copyright (c) 2014, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jpackage.internal;
+
+import java.io.File;
+import java.io.IOException;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.ResourceBundle;
+
+import static jdk.jpackage.internal.StandardBundlerParam.*;
+import static jdk.jpackage.internal.MacAppBundler.*;
+
+public class MacAppStoreBundler extends MacBaseInstallerBundler {
+
+    private static final ResourceBundle I18N = ResourceBundle.getBundle(
+            "jdk.jpackage.internal.resources.MacResources");
+
+    private static final String TEMPLATE_BUNDLE_ICON_HIDPI =
+            "GenericAppHiDPI.icns";
+    private final static String DEFAULT_ENTITLEMENTS =
+            "MacAppStore.entitlements";
+    private final static String DEFAULT_INHERIT_ENTITLEMENTS =
+            "MacAppStore_Inherit.entitlements";
+
+    public static final BundlerParamInfo<String> MAC_APP_STORE_APP_SIGNING_KEY =
+            new StandardBundlerParam<>(
+            I18N.getString("param.signing-key-app.name"),
+            I18N.getString("param.signing-key-app.description"),
+            "mac.signing-key-app",
+            String.class,
+            params -> {
+                    String result = MacBaseInstallerBundler.findKey(
+                            "3rd Party Mac Developer Application: " +
+                                    SIGNING_KEY_USER.fetchFrom(params),
+                            SIGNING_KEYCHAIN.fetchFrom(params),
+                            VERBOSE.fetchFrom(params));
+                    if (result != null) {
+                        MacCertificate certificate = new MacCertificate(result,
+                                VERBOSE.fetchFrom(params));
+
+                        if (!certificate.isValid()) {
+                            Log.error(MessageFormat.format(
+                                    I18N.getString("error.certificate.expired"),
+                                    result));
+                        }
+                    }
+
+                    return result;
+                },
+            (s, p) -> s);
+
+    public static final BundlerParamInfo<String> MAC_APP_STORE_PKG_SIGNING_KEY =
+            new StandardBundlerParam<>(
+            I18N.getString("param.signing-key-pkg.name"),
+            I18N.getString("param.signing-key-pkg.description"),
+            "mac.signing-key-pkg",
+            String.class,
+            params -> {
+                    String result = MacBaseInstallerBundler.findKey(
+                            "3rd Party Mac Developer Installer: " +
+                                    SIGNING_KEY_USER.fetchFrom(params),
+                            SIGNING_KEYCHAIN.fetchFrom(params),
+                            VERBOSE.fetchFrom(params));
+
+                    if (result != null) {
+                        MacCertificate certificate = new MacCertificate(
+                                result, VERBOSE.fetchFrom(params));
+
+                        if (!certificate.isValid()) {
+                            Log.error(MessageFormat.format(
+                                    I18N.getString("error.certificate.expired"),
+                                    result));
+                        }
+                    }
+
+                    return result;
+                },
+            (s, p) -> s);
+
+    public static final StandardBundlerParam<File> MAC_APP_STORE_ENTITLEMENTS  =
+            new StandardBundlerParam<>(
+            I18N.getString("param.mac-app-store-entitlements.name"),
+            I18N.getString("param.mac-app-store-entitlements.description"),
+            Arguments.CLIOptions.MAC_APP_STORE_ENTITLEMENTS.getId(),
+            File.class,
+            params -> null,
+            (s, p) -> new File(s));
+
+    public static final BundlerParamInfo<String> INSTALLER_SUFFIX =
+            new StandardBundlerParam<> (
+            I18N.getString("param.installer-suffix.name"),
+            I18N.getString("param.installer-suffix.description"),
+            "mac.app-store.installerName.suffix",
+            String.class,
+            params -> "-MacAppStore",
+            (s, p) -> s);
+
+    //@Override
+    public File bundle(Map<String, ? super Object> p,
+            File outdir) throws PackagerException {
+        Log.verbose(MessageFormat.format(I18N.getString(
+                "message.building-bundle"), APP_NAME.fetchFrom(p)));
+        if (!outdir.isDirectory() && !outdir.mkdirs()) {
+            throw new PackagerException(
+                    "error.cannot-create-output-dir",
+                     outdir.getAbsolutePath());
+        }
+        if (!outdir.canWrite()) {
+            throw new PackagerException(
+                    "error.cannot-write-to-output-dir",
+                    outdir.getAbsolutePath());
+        }
+
+        // first, load in some overrides
+        // icns needs @2 versions, so load in the @2 default
+        p.put(DEFAULT_ICNS_ICON.getID(), TEMPLATE_BUNDLE_ICON_HIDPI);
+
+        // now we create the app
+        File appImageDir = APP_IMAGE_BUILD_ROOT.fetchFrom(p);
+        try {
+            appImageDir.mkdirs();
+
+            try {
+                MacAppImageBuilder.addNewKeychain(p);
+            } catch (InterruptedException e) {
+                Log.error(e.getMessage());
+            }
+            // first, make sure we don't use the local signing key
+            p.put(DEVELOPER_ID_APP_SIGNING_KEY.getID(), null);
+            File appLocation = prepareAppBundle(p, false);
+
+            prepareEntitlements(p);
+
+            String signingIdentity = MAC_APP_STORE_APP_SIGNING_KEY.fetchFrom(p);
+            String identifierPrefix = BUNDLE_ID_SIGNING_PREFIX.fetchFrom(p);
+            String entitlementsFile = getConfig_Entitlements(p).toString();
+            String inheritEntitlements =
+                    getConfig_Inherit_Entitlements(p).toString();
+
+            MacAppImageBuilder.signAppBundle(p, appLocation.toPath(),
+                    signingIdentity, identifierPrefix,
+                    entitlementsFile, inheritEntitlements);
+            MacAppImageBuilder.restoreKeychainList(p);
+
+            ProcessBuilder pb;
+
+            // create the final pkg file
+            File finalPKG = new File(outdir, INSTALLER_NAME.fetchFrom(p)
+                    + INSTALLER_SUFFIX.fetchFrom(p)
+                    + ".pkg");
+            outdir.mkdirs();
+
+            String installIdentify =
+                    MAC_APP_STORE_PKG_SIGNING_KEY.fetchFrom(p);
+
+            List<String> buildOptions = new ArrayList<>();
+            buildOptions.add("productbuild");
+            buildOptions.add("--component");
+            buildOptions.add(appLocation.toString());
+            buildOptions.add("/Applications");
+            buildOptions.add("--sign");
+            buildOptions.add(installIdentify);
+            buildOptions.add("--product");
+            buildOptions.add(appLocation + "/Contents/Info.plist");
+            String keychainName = SIGNING_KEYCHAIN.fetchFrom(p);
+            if (keychainName != null && !keychainName.isEmpty()) {
+                buildOptions.add("--keychain");
+                buildOptions.add(keychainName);
+            }
+            buildOptions.add(finalPKG.getAbsolutePath());
+
+            pb = new ProcessBuilder(buildOptions);
+
+            IOUtils.exec(pb, false);
+            return finalPKG;
+        } catch (PackagerException pe) {
+            throw pe;
+        } catch (Exception ex) {
+            Log.verbose(ex);
+            throw new PackagerException(ex);
+        }
+    }
+
+    private File getConfig_Entitlements(Map<String, ? super Object> params) {
+        return new File(CONFIG_ROOT.fetchFrom(params),
+                APP_NAME.fetchFrom(params) + ".entitlements");
+    }
+
+    private File getConfig_Inherit_Entitlements(
+            Map<String, ? super Object> params) {
+        return new File(CONFIG_ROOT.fetchFrom(params),
+                APP_NAME.fetchFrom(params) + "_Inherit.entitlements");
+    }
+
+    private void prepareEntitlements(Map<String, ? super Object> params)
+            throws IOException {
+        File entitlements = MAC_APP_STORE_ENTITLEMENTS.fetchFrom(params);
+        if (entitlements == null || !entitlements.exists()) {
+            fetchResource(getEntitlementsFileName(params),
+                    I18N.getString("resource.mac-app-store-entitlements"),
+                    DEFAULT_ENTITLEMENTS,
+                    getConfig_Entitlements(params),
+                    VERBOSE.fetchFrom(params),
+                    RESOURCE_DIR.fetchFrom(params));
+        } else {
+            fetchResource(getEntitlementsFileName(params),
+                    I18N.getString("resource.mac-app-store-entitlements"),
+                    entitlements,
+                    getConfig_Entitlements(params),
+                    VERBOSE.fetchFrom(params),
+                    RESOURCE_DIR.fetchFrom(params));
+        }
+        fetchResource(getInheritEntitlementsFileName(params),
+                I18N.getString("resource.mac-app-store-inherit-entitlements"),
+                DEFAULT_INHERIT_ENTITLEMENTS,
+                getConfig_Inherit_Entitlements(params),
+                VERBOSE.fetchFrom(params),
+                RESOURCE_DIR.fetchFrom(params));
+    }
+
+    private String getEntitlementsFileName(Map<String, ? super Object> params) {
+        return APP_NAME.fetchFrom(params) + ".entitlements";
+    }
+
+    private String getInheritEntitlementsFileName(
+            Map<String, ? super Object> params) {
+        return APP_NAME.fetchFrom(params) + "_Inherit.entitlements";
+    }
+
+
+    ///////////////////////////////////////////////////////////////////////
+    // Implement Bundler
+    ///////////////////////////////////////////////////////////////////////
+
+    @Override
+    public String getName() {
+        return I18N.getString("store.bundler.name");
+    }
+
+    @Override
+    public String getDescription() {
+        return I18N.getString("store.bundler.description");
+    }
+
+    @Override
+    public String getID() {
+        return "mac.appStore";
+    }
+
+    @Override
+    public Collection<BundlerParamInfo<?>> getBundleParameters() {
+        Collection<BundlerParamInfo<?>> results = new LinkedHashSet<>();
+        results.addAll(getAppBundleParameters());
+        results.addAll(getMacAppStoreBundleParameters());
+        return results;
+    }
+
+    public Collection<BundlerParamInfo<?>> getMacAppStoreBundleParameters() {
+        Collection<BundlerParamInfo<?>> results = new LinkedHashSet<>();
+
+        results.addAll(getAppBundleParameters());
+        results.remove(DEVELOPER_ID_APP_SIGNING_KEY);
+        results.addAll(Arrays.asList(
+                INSTALLER_SUFFIX,
+                MAC_APP_STORE_APP_SIGNING_KEY,
+                MAC_APP_STORE_ENTITLEMENTS,
+                MAC_APP_STORE_PKG_SIGNING_KEY,
+                SIGNING_KEYCHAIN
+        ));
+
+        return results;
+    }
+
+    @Override
+    public boolean validate(Map<String, ? super Object> params)
+            throws UnsupportedPlatformException, ConfigException {
+        try {
+            if (Platform.getPlatform() != Platform.MAC) {
+                throw new UnsupportedPlatformException();
+            }
+
+            if (params == null) {
+                throw new ConfigException(
+                        I18N.getString("error.parameters-null"),
+                        I18N.getString("error.parameters-null.advice"));
+            }
+
+            // hdiutil is always available so there's no need to test for
+            // availability.
+            // run basic validation to ensure requirements are met
+
+            // TODO Mac App Store apps cannot use the system runtime
+
+            // we are not interested in return code, only possible exception
+            validateAppImageAndBundeler(params);
+
+            // reject explicitly set to not sign
+            if (!Optional.ofNullable(MacAppImageBuilder.
+                    SIGN_BUNDLE.fetchFrom(params)).orElse(Boolean.TRUE)) {
+                throw new ConfigException(
+                        I18N.getString("error.must-sign-app-store"),
+                        I18N.getString("error.must-sign-app-store.advice"));
+            }
+
+            // make sure we have settings for signatures
+            if (MAC_APP_STORE_APP_SIGNING_KEY.fetchFrom(params) == null) {
+                throw new ConfigException(
+                        I18N.getString("error.no-app-signing-key"),
+                        I18N.getString("error.no-app-signing-key.advice"));
+            }
+            if (MAC_APP_STORE_PKG_SIGNING_KEY.fetchFrom(params) == null) {
+                throw new ConfigException(
+                        I18N.getString("error.no-pkg-signing-key"),
+                        I18N.getString("error.no-pkg-signing-key.advice"));
+            }
+
+            // things we could check...
+            // check the icons, make sure it has hidpi icons
+            // check the category,
+            // make sure it fits in the list apple has provided
+            // validate bundle identifier is reverse dns
+            // check for \a+\.\a+\..
+
+            return true;
+        } catch (RuntimeException re) {
+            if (re.getCause() instanceof ConfigException) {
+                throw (ConfigException) re.getCause();
+            } else {
+                throw new ConfigException(re);
+            }
+        }
+    }
+
+    @Override
+    public File execute(Map<String, ? super Object> params,
+            File outputParentDir) throws PackagerException {
+        return bundle(params, outputParentDir);
+    }
+
+    @Override
+    public boolean supported(boolean runtimeInstaller) {
+        return (!runtimeInstaller &&
+                Platform.getPlatform() == Platform.MAC);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacBaseInstallerBundler.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,222 @@
+/*
+ * Copyright (c) 2014, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jpackage.internal;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.nio.file.Files;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.ResourceBundle;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import static jdk.jpackage.internal.StandardBundlerParam.*;
+
+public abstract class MacBaseInstallerBundler extends AbstractBundler {
+
+    private static final ResourceBundle I18N = ResourceBundle.getBundle(
+            "jdk.jpackage.internal.resources.MacResources");
+
+    // This could be generalized more to be for any type of Image Bundler
+    public static final BundlerParamInfo<MacAppBundler> APP_BUNDLER =
+            new StandardBundlerParam<>(
+            I18N.getString("param.app-bundler.name"),
+            I18N.getString("param.app-bundle.description"),
+            "mac.app.bundler",
+            MacAppBundler.class,
+            params -> new MacAppBundler(),
+            (s, p) -> null);
+
+    public final BundlerParamInfo<File> APP_IMAGE_BUILD_ROOT =
+            new StandardBundlerParam<>(
+            I18N.getString("param.app-image-build-root.name"),
+            I18N.getString("param.app-image-build-root.description"),
+            "mac.app.imageRoot",
+            File.class,
+            params -> {
+                File imageDir = IMAGES_ROOT.fetchFrom(params);
+                if (!imageDir.exists()) imageDir.mkdirs();
+                try {
+                    return Files.createTempDirectory(
+                            imageDir.toPath(), "image-").toFile();
+                } catch (IOException e) {
+                    return new File(imageDir, getID()+ ".image");
+                }
+            },
+            (s, p) -> new File(s));
+
+    public static final BundlerParamInfo<String> SIGNING_KEY_USER =
+            new StandardBundlerParam<>(
+            I18N.getString("param.signing-key-name.name"),
+            I18N.getString("param.signing-key-name.description"),
+            Arguments.CLIOptions.MAC_SIGNING_KEY_NAME.getId(),
+            String.class,
+            params -> "",
+            null);
+
+    public static final BundlerParamInfo<String> SIGNING_KEYCHAIN =
+            new StandardBundlerParam<>(
+            I18N.getString("param.signing-keychain.name"),
+            I18N.getString("param.signing-keychain.description"),
+            Arguments.CLIOptions.MAC_SIGNING_KEYCHAIN.getId(),
+            String.class,
+            params -> "",
+            null);
+
+    public static final BundlerParamInfo<String> INSTALLER_NAME =
+            new StandardBundlerParam<> (
+            I18N.getString("param.installer-name.name"),
+            I18N.getString("param.installer-name.description"),
+            "mac.installerName",
+            String.class,
+            params -> {
+                String nm = APP_NAME.fetchFrom(params);
+                if (nm == null) return null;
+
+                String version = VERSION.fetchFrom(params);
+                if (version == null) {
+                    return nm;
+                } else {
+                    return nm + "-" + version;
+                }
+            },
+            (s, p) -> s);
+
+    protected void validateAppImageAndBundeler(
+            Map<String, ? super Object> params)
+            throws ConfigException, UnsupportedPlatformException {
+        if (PREDEFINED_APP_IMAGE.fetchFrom(params) != null) {
+            File applicationImage = PREDEFINED_APP_IMAGE.fetchFrom(params);
+            if (!applicationImage.exists()) {
+                throw new ConfigException(
+                        MessageFormat.format(I18N.getString(
+                                "message.app-image-dir-does-not-exist"),
+                                PREDEFINED_APP_IMAGE.getID(),
+                                applicationImage.toString()),
+                        MessageFormat.format(I18N.getString(
+                                "message.app-image-dir-does-not-exist.advice"),
+                                PREDEFINED_APP_IMAGE.getID()));
+            }
+            if (APP_NAME.fetchFrom(params) == null) {
+                throw new ConfigException(
+                        I18N.getString("message.app-image-requires-app-name"),
+                        I18N.getString(
+                            "message.app-image-requires-app-name.advice"));
+            }
+            if (IDENTIFIER.fetchFrom(params) == null) {
+                throw new ConfigException(
+                        I18N.getString("message.app-image-requires-identifier"),
+                        I18N.getString(
+                            "message.app-image-requires-identifier.advice"));
+            }
+        } else {
+            APP_BUNDLER.fetchFrom(params).validate(params);
+        }
+    }
+
+    protected File prepareAppBundle(Map<String, ? super Object> p,
+            boolean pkg) throws PackagerException {
+        File predefinedImage = StandardBundlerParam.getPredefinedAppImage(p);
+        if (predefinedImage != null) {
+            return predefinedImage;
+        }
+        File appImageRoot = APP_IMAGE_BUILD_ROOT.fetchFrom(p);
+        if (pkg) {
+            // create pkg in dmg
+            return new MacPkgBundler().bundle(p, appImageRoot);
+        } else {
+            return APP_BUNDLER.fetchFrom(p).doBundle(p, appImageRoot, true);
+        }
+    }
+
+    @Override
+    public Collection<BundlerParamInfo<?>> getBundleParameters() {
+        Collection<BundlerParamInfo<?>> results = new LinkedHashSet<>();
+
+        results.addAll(MacAppBundler.getAppBundleParameters());
+        results.addAll(Arrays.asList(
+                APP_BUNDLER,
+                CONFIG_ROOT,
+                APP_IMAGE_BUILD_ROOT,
+                PREDEFINED_APP_IMAGE
+        ));
+
+        return results;
+    }
+
+    @Override
+    public String getBundleType() {
+        return "INSTALLER";
+    }
+
+    public static String findKey(String key, String keychainName,
+            boolean verbose) {
+        if (Platform.getPlatform() != Platform.MAC) {
+            return null;
+        }
+
+        try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
+                PrintStream ps = new PrintStream(baos)) {
+            List<String> searchOptions = new ArrayList<>();
+            searchOptions.add("security");
+            searchOptions.add("find-certificate");
+            searchOptions.add("-c");
+            searchOptions.add(key);
+            searchOptions.add("-a");
+            if (keychainName != null && !keychainName.isEmpty()) {
+                searchOptions.add(keychainName);
+            }
+
+            ProcessBuilder pb = new ProcessBuilder(searchOptions);
+
+            IOUtils.exec(pb, verbose, false, ps);
+            Pattern p = Pattern.compile("\"alis\"<blob>=\"([^\"]+)\"");
+            Matcher m = p.matcher(baos.toString());
+            if (!m.find()) {
+                Log.error("Did not find a key matching '" + key + "'");
+                return null;
+            }
+            String matchedKey = m.group(1);
+            if (m.find()) {
+                Log.error("Found more than one key matching '"  + key + "'");
+                return null;
+            }
+            Log.debug("Using key '" + matchedKey + "'");
+            return matchedKey;
+        } catch (IOException ioe) {
+            Log.verbose(ioe);
+            return null;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacCertificate.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jpackage.internal;
+
+import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintStream;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+
+final class MacCertificate {
+    private final String certificate;
+    private final boolean verbose;
+
+    MacCertificate(String certificate) {
+        this.certificate = certificate;
+        this.verbose = false;
+    }
+
+    MacCertificate(String certificate, boolean verbose) {
+        this.certificate = certificate;
+        this.verbose = verbose;
+    }
+
+    boolean isValid() {
+        return verifyCertificate(this.certificate, verbose);
+    }
+
+    private static File findCertificate(String certificate, boolean verbose) {
+        File result = null;
+
+        List<String> args = new ArrayList<>();
+        args.add("security");
+        args.add("find-certificate");
+        args.add("-c");
+        args.add(certificate);
+        args.add("-a");
+        args.add("-p");
+
+        try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
+                PrintStream ps = new PrintStream(baos)) {
+            ProcessBuilder security = new ProcessBuilder(args);
+            IOUtils.exec(security, verbose, false, ps);
+
+            File output = File.createTempFile("tempfile", ".tmp");
+            PrintStream p = new PrintStream(
+                    new BufferedOutputStream(
+                            new FileOutputStream(output, true)));
+            BufferedReader bfReader = new BufferedReader(
+                    new InputStreamReader(
+                            new ByteArrayInputStream(baos.toByteArray())));
+            String line = null;
+
+            while((line = bfReader.readLine()) != null){
+                p.println(line);
+            }
+
+            p.close();
+            result = output;
+        }
+        catch (IOException ignored) {}
+
+        return result;
+    }
+
+    private static Date findCertificateDate(String filename, boolean verbose) {
+        Date result = null;
+
+        List<String> args = new ArrayList<>();
+        args.add("/usr/bin/openssl");
+        args.add("x509");
+        args.add("-noout");
+        args.add("-enddate");
+        args.add("-in");
+        args.add(filename);
+
+        try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
+                PrintStream ps = new PrintStream(baos)) {
+            ProcessBuilder security = new ProcessBuilder(args);
+            IOUtils.exec(security, verbose, false, ps);
+            String output = baos.toString();
+            output = output.substring(output.indexOf("=") + 1);
+            DateFormat df = new SimpleDateFormat(
+                    "MMM dd kk:mm:ss yyyy z", Locale.ENGLISH);
+            result = df.parse(output);
+        } catch (IOException | ParseException ex) {
+            Log.debug(ex);
+        }
+
+        return result;
+    }
+
+    private static boolean verifyCertificate(
+            String certificate, boolean verbose) {
+        boolean result = false;
+
+        try {
+            File file = null;
+            Date certificateDate = null;
+
+            try {
+                file = findCertificate(certificate, verbose);
+
+                if (file != null) {
+                    certificateDate = findCertificateDate(
+                            file.getCanonicalPath(), verbose);
+                }
+            }
+            finally {
+                if (file != null) {
+                    file.delete();
+                }
+            }
+
+            if (certificateDate != null) {
+                Calendar c = Calendar.getInstance();
+                Date today = c.getTime();
+
+                if (certificateDate.after(today)) {
+                    result = true;
+                }
+            }
+        }
+        catch (IOException ignored) {}
+
+        return result;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgBundler.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,485 @@
+/*
+ * Copyright (c) 2012, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jpackage.internal;
+
+import java.io.*;
+import java.nio.file.Files;
+import java.text.MessageFormat;
+import java.util.*;
+
+import static jdk.jpackage.internal.StandardBundlerParam.*;
+
+public class MacDmgBundler extends MacBaseInstallerBundler {
+
+    private static final ResourceBundle I18N = ResourceBundle.getBundle(
+            "jdk.jpackage.internal.resources.MacResources");
+
+    static final String DEFAULT_BACKGROUND_IMAGE="background_dmg.png";
+    static final String DEFAULT_DMG_SETUP_SCRIPT="DMGsetup.scpt";
+    static final String TEMPLATE_BUNDLE_ICON = "GenericApp.icns";
+
+    static final String DEFAULT_LICENSE_PLIST="lic_template.plist";
+
+    public static final BundlerParamInfo<String> INSTALLER_SUFFIX =
+            new StandardBundlerParam<> (
+            I18N.getString("param.installer-suffix.name"),
+            I18N.getString("param.installer-suffix.description"),
+            "mac.dmg.installerName.suffix",
+            String.class,
+            params -> "",
+            (s, p) -> s);
+
+    public File bundle(Map<String, ? super Object> params,
+            File outdir) throws PackagerException {
+        Log.verbose(MessageFormat.format(I18N.getString("message.building-dmg"),
+                APP_NAME.fetchFrom(params)));
+        if (!outdir.isDirectory() && !outdir.mkdirs()) {
+            throw new PackagerException(
+                    "error.cannot-create-output-dir",
+                    outdir.getAbsolutePath());
+        }
+        if (!outdir.canWrite()) {
+            throw new PackagerException(
+                    "error.cannot-write-to-output-dir",
+                    outdir.getAbsolutePath());
+        }
+
+        File appImageDir = APP_IMAGE_BUILD_ROOT.fetchFrom(params);
+        try {
+            appImageDir.mkdirs();
+
+            if (prepareAppBundle(params, true) != null &&
+                    prepareConfigFiles(params)) {
+                File configScript = getConfig_Script(params);
+                if (configScript.exists()) {
+                    Log.verbose(MessageFormat.format(
+                            I18N.getString("message.running-script"),
+                            configScript.getAbsolutePath()));
+                    IOUtils.run("bash", configScript, false);
+                }
+
+                return buildDMG(params, outdir);
+            }
+            return null;
+        } catch (IOException ex) {
+            Log.verbose(ex);
+            throw new PackagerException(ex);
+        }
+    }
+
+    private static final String hdiutil = "/usr/bin/hdiutil";
+
+    private void prepareDMGSetupScript(String volumeName,
+            Map<String, ? super Object> p) throws IOException {
+        File dmgSetup = getConfig_VolumeScript(p);
+        Log.verbose(MessageFormat.format(
+                I18N.getString("message.preparing-dmg-setup"),
+                dmgSetup.getAbsolutePath()));
+
+        //prepare config for exe
+        Map<String, String> data = new HashMap<>();
+        data.put("DEPLOY_ACTUAL_VOLUME_NAME", volumeName);
+        data.put("DEPLOY_APPLICATION_NAME", APP_NAME.fetchFrom(p));
+
+        data.put("DEPLOY_INSTALL_LOCATION", "(path to desktop folder)");
+        data.put("DEPLOY_INSTALL_NAME", "Desktop");
+
+        Writer w = new BufferedWriter(new FileWriter(dmgSetup));
+        w.write(preprocessTextResource(dmgSetup.getName(),
+                I18N.getString("resource.dmg-setup-script"),
+                        DEFAULT_DMG_SETUP_SCRIPT, data, VERBOSE.fetchFrom(p),
+                RESOURCE_DIR.fetchFrom(p)));
+        w.close();
+    }
+
+    private File getConfig_VolumeScript(Map<String, ? super Object> params) {
+        return new File(CONFIG_ROOT.fetchFrom(params),
+                APP_NAME.fetchFrom(params) + "-dmg-setup.scpt");
+    }
+
+    private File getConfig_VolumeBackground(
+            Map<String, ? super Object> params) {
+        return new File(CONFIG_ROOT.fetchFrom(params),
+                APP_NAME.fetchFrom(params) + "-background.png");
+    }
+
+    private File getConfig_VolumeIcon(Map<String, ? super Object> params) {
+        return new File(CONFIG_ROOT.fetchFrom(params),
+                APP_NAME.fetchFrom(params) + "-volume.icns");
+    }
+
+    private File getConfig_LicenseFile(Map<String, ? super Object> params) {
+        return new File(CONFIG_ROOT.fetchFrom(params),
+                APP_NAME.fetchFrom(params) + "-license.plist");
+    }
+
+    private void prepareLicense(Map<String, ? super Object> params) {
+        try {
+            String licFileStr = LICENSE_FILE.fetchFrom(params);
+            if (licFileStr == null) {
+                return;
+            }
+
+            File licFile = new File(licFileStr);
+            byte[] licenseContentOriginal = Files.readAllBytes(licFile.toPath());
+            String licenseInBase64 =
+                    Base64.getEncoder().encodeToString(licenseContentOriginal);
+
+            Map<String, String> data = new HashMap<>();
+            data.put("APPLICATION_LICENSE_TEXT", licenseInBase64);
+
+            Writer w = new BufferedWriter(
+                    new FileWriter(getConfig_LicenseFile(params)));
+            w.write(preprocessTextResource(
+                    getConfig_LicenseFile(params).getName(),
+                    I18N.getString("resource.license-setup"),
+                    DEFAULT_LICENSE_PLIST, data, VERBOSE.fetchFrom(params),
+                    RESOURCE_DIR.fetchFrom(params)));
+            w.close();
+
+        } catch (IOException ex) {
+            Log.verbose(ex);
+        }
+    }
+
+    private boolean prepareConfigFiles(Map<String, ? super Object> params)
+            throws IOException {
+        File bgTarget = getConfig_VolumeBackground(params);
+        fetchResource(bgTarget.getName(),
+                I18N.getString("resource.dmg-background"),
+                DEFAULT_BACKGROUND_IMAGE,
+                bgTarget,
+                VERBOSE.fetchFrom(params),
+                RESOURCE_DIR.fetchFrom(params));
+
+        File iconTarget = getConfig_VolumeIcon(params);
+        if (MacAppBundler.ICON_ICNS.fetchFrom(params) == null ||
+                !MacAppBundler.ICON_ICNS.fetchFrom(params).exists()) {
+            fetchResource(iconTarget.getName(),
+                    I18N.getString("resource.volume-icon"),
+                    TEMPLATE_BUNDLE_ICON,
+                    iconTarget,
+                    VERBOSE.fetchFrom(params),
+                    RESOURCE_DIR.fetchFrom(params));
+        } else {
+            fetchResource(iconTarget.getName(),
+                    I18N.getString("resource.volume-icon"),
+                    MacAppBundler.ICON_ICNS.fetchFrom(params),
+                    iconTarget,
+                    VERBOSE.fetchFrom(params),
+                    RESOURCE_DIR.fetchFrom(params));
+        }
+
+
+        fetchResource(getConfig_Script(params).getName(),
+                I18N.getString("resource.post-install-script"),
+                (String) null,
+                getConfig_Script(params),
+                VERBOSE.fetchFrom(params),
+                RESOURCE_DIR.fetchFrom(params));
+
+        prepareLicense(params);
+
+        // In theory we need to extract name from results of attach command
+        // However, this will be a problem for customization as name will
+        // possibly change every time and developer will not be able to fix it
+        // As we are using tmp dir chance we get "different" name are low =>
+        // Use fixed name we used for bundle
+        prepareDMGSetupScript(APP_NAME.fetchFrom(params), params);
+
+        return true;
+    }
+
+    // name of post-image script
+    private File getConfig_Script(Map<String, ? super Object> params) {
+        return new File(CONFIG_ROOT.fetchFrom(params),
+                APP_NAME.fetchFrom(params) + "-post-image.sh");
+    }
+
+    // Location of SetFile utility may be different depending on MacOS version
+    // We look for several known places and if none of them work will
+    // try ot find it
+    private String findSetFileUtility() {
+        String typicalPaths[] = {"/Developer/Tools/SetFile",
+                "/usr/bin/SetFile", "/Developer/usr/bin/SetFile"};
+
+        for (String path: typicalPaths) {
+            File f = new File(path);
+            if (f.exists() && f.canExecute()) {
+                return path;
+            }
+        }
+
+        // generic find attempt
+        try {
+            ProcessBuilder pb = new ProcessBuilder("xcrun", "-find", "SetFile");
+            Process p = pb.start();
+            InputStreamReader isr = new InputStreamReader(p.getInputStream());
+            BufferedReader br = new BufferedReader(isr);
+            String lineRead = br.readLine();
+            if (lineRead != null) {
+                File f = new File(lineRead);
+                if (f.exists() && f.canExecute()) {
+                    return f.getAbsolutePath();
+                }
+            }
+        } catch (IOException ignored) {}
+
+        return null;
+    }
+
+    private File buildDMG(
+            Map<String, ? super Object> p, File outdir)
+            throws IOException {
+        File imagesRoot = IMAGES_ROOT.fetchFrom(p);
+        if (!imagesRoot.exists()) imagesRoot.mkdirs();
+
+        File protoDMG = new File(imagesRoot, APP_NAME.fetchFrom(p) +"-tmp.dmg");
+        File finalDMG = new File(outdir, INSTALLER_NAME.fetchFrom(p)
+                + INSTALLER_SUFFIX.fetchFrom(p)
+                + ".dmg");
+
+        File srcFolder = APP_IMAGE_BUILD_ROOT.fetchFrom(p);
+        File predefinedImage = StandardBundlerParam.getPredefinedAppImage(p);
+        if (predefinedImage != null) {
+            srcFolder = predefinedImage;
+        }
+
+        Log.verbose(MessageFormat.format(I18N.getString(
+                "message.creating-dmg-file"), finalDMG.getAbsolutePath()));
+
+        protoDMG.delete();
+        if (finalDMG.exists() && !finalDMG.delete()) {
+            throw new IOException(MessageFormat.format(I18N.getString(
+                    "message.dmg-cannot-be-overwritten"),
+                    finalDMG.getAbsolutePath()));
+        }
+
+        protoDMG.getParentFile().mkdirs();
+        finalDMG.getParentFile().mkdirs();
+
+        String hdiUtilVerbosityFlag = Log.isDebug() ? "-verbose" : "-quiet";
+
+        // create temp image
+        ProcessBuilder pb = new ProcessBuilder(
+                hdiutil,
+                "create",
+                hdiUtilVerbosityFlag,
+                "-srcfolder", srcFolder.getAbsolutePath(),
+                "-volname", APP_NAME.fetchFrom(p),
+                "-ov", protoDMG.getAbsolutePath(),
+                "-fs", "HFS+",
+                "-format", "UDRW");
+        IOUtils.exec(pb, false);
+
+        // mount temp image
+        pb = new ProcessBuilder(
+                hdiutil,
+                "attach",
+                protoDMG.getAbsolutePath(),
+                hdiUtilVerbosityFlag,
+                "-mountroot", imagesRoot.getAbsolutePath());
+        IOUtils.exec(pb, false);
+
+        File mountedRoot =
+                new File(imagesRoot.getAbsolutePath(), APP_NAME.fetchFrom(p));
+
+        // volume icon
+        File volumeIconFile = new File(mountedRoot, ".VolumeIcon.icns");
+        IOUtils.copyFile(getConfig_VolumeIcon(p),
+                volumeIconFile);
+
+        pb = new ProcessBuilder("osascript",
+                getConfig_VolumeScript(p).getAbsolutePath());
+        IOUtils.exec(pb, false);
+
+        // Indicate that we want a custom icon
+        // NB: attributes of the root directory are ignored
+        // when creating the volume
+        // Therefore we have to do this after we mount image
+        String setFileUtility = findSetFileUtility();
+        if (setFileUtility != null) {
+                //can not find utility => keep going without icon
+            try {
+                volumeIconFile.setWritable(true);
+                // The "creator" attribute on a file is a legacy attribute
+                // but it seems Finder excepts these bytes to be
+                // "icnC" for the volume icon
+                // http://endrift.com/blog/2010/06/14/dmg-files-volume-icons-cli
+                // (might not work on Mac 10.13 with old XCode)
+                pb = new ProcessBuilder(
+                        setFileUtility,
+                        "-c", "icnC",
+                        volumeIconFile.getAbsolutePath());
+                IOUtils.exec(pb, false);
+                volumeIconFile.setReadOnly();
+
+                pb = new ProcessBuilder(
+                        setFileUtility,
+                        "-a", "C",
+                        mountedRoot.getAbsolutePath());
+                IOUtils.exec(pb, false);
+            } catch (IOException ex) {
+                Log.error(ex.getMessage());
+                Log.verbose("Cannot enable custom icon using SetFile utility");
+            }
+        } else {
+            Log.verbose(
+                "Skip enabling custom icon as SetFile utility is not found");
+        }
+
+        // Detach the temporary image
+        pb = new ProcessBuilder(
+                hdiutil,
+                "detach",
+                hdiUtilVerbosityFlag,
+                mountedRoot.getAbsolutePath());
+        IOUtils.exec(pb, false);
+
+        // Compress it to a new image
+        pb = new ProcessBuilder(
+                hdiutil,
+                "convert",
+                protoDMG.getAbsolutePath(),
+                hdiUtilVerbosityFlag,
+                "-format", "UDZO",
+                "-o", finalDMG.getAbsolutePath());
+        IOUtils.exec(pb, false);
+
+        //add license if needed
+        if (getConfig_LicenseFile(p).exists()) {
+            //hdiutil unflatten your_image_file.dmg
+            pb = new ProcessBuilder(
+                    hdiutil,
+                    "unflatten",
+                    finalDMG.getAbsolutePath()
+            );
+            IOUtils.exec(pb, false);
+
+            //add license
+            pb = new ProcessBuilder(
+                    hdiutil,
+                    "udifrez",
+                    finalDMG.getAbsolutePath(),
+                    "-xml",
+                    getConfig_LicenseFile(p).getAbsolutePath()
+            );
+            IOUtils.exec(pb, false);
+
+            //hdiutil flatten your_image_file.dmg
+            pb = new ProcessBuilder(
+                    hdiutil,
+                    "flatten",
+                    finalDMG.getAbsolutePath()
+            );
+            IOUtils.exec(pb, false);
+
+        }
+
+        //Delete the temporary image
+        protoDMG.delete();
+
+        Log.verbose(MessageFormat.format(I18N.getString(
+                "message.output-to-location"),
+                APP_NAME.fetchFrom(p), finalDMG.getAbsolutePath()));
+
+        return finalDMG;
+    }
+
+
+    //////////////////////////////////////////////////////////////////////////
+    // Implement Bundler
+    //////////////////////////////////////////////////////////////////////////
+
+    @Override
+    public String getName() {
+        return I18N.getString("dmg.bundler.name");
+    }
+
+    @Override
+    public String getDescription() {
+        return I18N.getString("dmg.bundler.description");
+    }
+
+    @Override
+    public String getID() {
+        return "dmg";
+    }
+
+    @Override
+    public Collection<BundlerParamInfo<?>> getBundleParameters() {
+        Collection<BundlerParamInfo<?>> results = new LinkedHashSet<>();
+        results.addAll(MacAppBundler.getAppBundleParameters());
+        results.addAll(getDMGBundleParameters());
+        return results;
+    }
+
+    public Collection<BundlerParamInfo<?>> getDMGBundleParameters() {
+        Collection<BundlerParamInfo<?>> results = new LinkedHashSet<>();
+
+        results.addAll(MacAppBundler.getAppBundleParameters());
+        results.addAll(Arrays.asList(
+                INSTALLER_SUFFIX,
+                LICENSE_FILE
+        ));
+
+        return results;
+    }
+
+
+    @Override
+    public boolean validate(Map<String, ? super Object> params)
+            throws UnsupportedPlatformException, ConfigException {
+        try {
+            if (params == null) throw new ConfigException(
+                    I18N.getString("error.parameters-null"),
+                    I18N.getString("error.parameters-null.advice"));
+
+            //run basic validation to ensure requirements are met
+            //we are not interested in return code, only possible exception
+            validateAppImageAndBundeler(params);
+
+            return true;
+        } catch (RuntimeException re) {
+            if (re.getCause() instanceof ConfigException) {
+                throw (ConfigException) re.getCause();
+            } else {
+                throw new ConfigException(re);
+            }
+        }
+    }
+
+    @Override
+    public File execute(Map<String, ? super Object> params,
+            File outputParentDir) throws PackagerException {
+        return bundle(params, outputParentDir);
+    }
+
+    @Override
+    public boolean supported(boolean runtimeInstaller) {
+        return Platform.getPlatform() == Platform.MAC;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPkgBundler.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,548 @@
+/*
+ * Copyright (c) 2014, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jpackage.internal;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.net.URLEncoder;
+import java.nio.file.Files;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.ResourceBundle;
+
+import static jdk.jpackage.internal.StandardBundlerParam.*;
+import static jdk.jpackage.internal.MacBaseInstallerBundler.SIGNING_KEYCHAIN;
+import static jdk.jpackage.internal.MacBaseInstallerBundler.SIGNING_KEY_USER;
+
+public class MacPkgBundler extends MacBaseInstallerBundler {
+
+    private static final ResourceBundle I18N = ResourceBundle.getBundle(
+            "jdk.jpackage.internal.resources.MacResources");
+
+    private static final String DEFAULT_BACKGROUND_IMAGE = "background_pkg.png";
+
+    private static final String TEMPLATE_PREINSTALL_SCRIPT =
+            "preinstall.template";
+    private static final String TEMPLATE_POSTINSTALL_SCRIPT =
+            "postinstall.template";
+
+    private static final BundlerParamInfo<File> PACKAGES_ROOT =
+            new StandardBundlerParam<>(
+            I18N.getString("param.packages-root.name"),
+            I18N.getString("param.packages-root.description"),
+            "mac.pkg.packagesRoot",
+            File.class,
+            params -> {
+                File packagesRoot =
+                        new File(BUILD_ROOT.fetchFrom(params), "packages");
+                packagesRoot.mkdirs();
+                return packagesRoot;
+            },
+            (s, p) -> new File(s));
+
+
+    protected final BundlerParamInfo<File> SCRIPTS_DIR =
+            new StandardBundlerParam<>(
+            I18N.getString("param.scripts-dir.name"),
+            I18N.getString("param.scripts-dir.description"),
+            "mac.pkg.scriptsDir",
+            File.class,
+            params -> {
+                File scriptsDir =
+                        new File(CONFIG_ROOT.fetchFrom(params), "scripts");
+                scriptsDir.mkdirs();
+                return scriptsDir;
+            },
+            (s, p) -> new File(s));
+
+    public static final
+            BundlerParamInfo<String> DEVELOPER_ID_INSTALLER_SIGNING_KEY =
+            new StandardBundlerParam<>(
+            I18N.getString("param.signing-key-developer-id-installer.name"),
+            I18N.getString(
+            "param.signing-key-developer-id-installer.description"),
+            "mac.signing-key-developer-id-installer",
+            String.class,
+            params -> {
+                    String result = MacBaseInstallerBundler.findKey(
+                            "Developer ID Installer: "
+                            + SIGNING_KEY_USER.fetchFrom(params),
+                            SIGNING_KEYCHAIN.fetchFrom(params),
+                            VERBOSE.fetchFrom(params));
+                    if (result != null) {
+                        MacCertificate certificate = new MacCertificate(
+                                result, VERBOSE.fetchFrom(params));
+
+                        if (!certificate.isValid()) {
+                            Log.error(MessageFormat.format(
+                                    I18N.getString("error.certificate.expired"),
+                                    result));
+                        }
+                    }
+
+                    return result;
+                },
+            (s, p) -> s);
+
+    public static final BundlerParamInfo<String> MAC_INSTALL_DIR =
+            new StandardBundlerParam<>(
+            I18N.getString("param.mac-install-dir.name"),
+            I18N.getString("param.mac-install-dir.description"),
+            "mac-install-dir",
+            String.class,
+             params -> {
+                 String dir = INSTALL_DIR.fetchFrom(params);
+                 return (dir != null) ? dir : "/Applications";
+             },
+            (s, p) -> s
+    );
+
+    public static final BundlerParamInfo<String> INSTALLER_SUFFIX =
+            new StandardBundlerParam<> (
+            I18N.getString("param.installer-suffix.name"),
+            I18N.getString("param.installer-suffix.description"),
+            "mac.pkg.installerName.suffix",
+            String.class,
+            params -> "",
+            (s, p) -> s);
+
+    public File bundle(Map<String, ? super Object> params,
+            File outdir) throws PackagerException {
+        Log.verbose(MessageFormat.format(I18N.getString("message.building-pkg"),
+                APP_NAME.fetchFrom(params)));
+        if (!outdir.isDirectory() && !outdir.mkdirs()) {
+            throw new PackagerException(
+                    "error.cannot-create-output-dir",
+                    outdir.getAbsolutePath());
+        }
+        if (!outdir.canWrite()) {
+            throw new PackagerException(
+                    "error.cannot-write-to-output-dir",
+                    outdir.getAbsolutePath());
+        }
+
+        File appImageDir = null;
+        try {
+            appImageDir = prepareAppBundle(params, false);
+
+            if (appImageDir != null && prepareConfigFiles(params)) {
+
+                File configScript = getConfig_Script(params);
+                if (configScript.exists()) {
+                    Log.verbose(MessageFormat.format(I18N.getString(
+                            "message.running-script"),
+                            configScript.getAbsolutePath()));
+                    IOUtils.run("bash", configScript, false);
+                }
+
+                return createPKG(params, outdir, appImageDir);
+            }
+            return null;
+        } catch (IOException ex) {
+            Log.verbose(ex);
+            throw new PackagerException(ex);
+        }
+    }
+
+    private File getPackages_AppPackage(Map<String, ? super Object> params) {
+        return new File(PACKAGES_ROOT.fetchFrom(params),
+                APP_NAME.fetchFrom(params) + "-app.pkg");
+    }
+
+    private File getPackages_DaemonPackage(Map<String, ? super Object> params) {
+        return new File(PACKAGES_ROOT.fetchFrom(params),
+                APP_NAME.fetchFrom(params) + "-daemon.pkg");
+    }
+
+    private File getConfig_DistributionXMLFile(
+            Map<String, ? super Object> params) {
+        return new File(CONFIG_ROOT.fetchFrom(params), "distribution.dist");
+    }
+
+    private File getConfig_BackgroundImage(Map<String, ? super Object> params) {
+        return new File(CONFIG_ROOT.fetchFrom(params),
+                APP_NAME.fetchFrom(params) + "-background.png");
+    }
+
+    private File getScripts_PreinstallFile(Map<String, ? super Object> params) {
+        return new File(SCRIPTS_DIR.fetchFrom(params), "preinstall");
+    }
+
+    private File getScripts_PostinstallFile(
+            Map<String, ? super Object> params) {
+        return new File(SCRIPTS_DIR.fetchFrom(params), "postinstall");
+    }
+
+    private String getAppIdentifier(Map<String, ? super Object> params) {
+        return IDENTIFIER.fetchFrom(params);
+    }
+
+    private String getDaemonIdentifier(Map<String, ? super Object> params) {
+        return IDENTIFIER.fetchFrom(params) + ".daemon";
+    }
+
+    private void preparePackageScripts(Map<String, ? super Object> params)
+            throws IOException {
+        Log.verbose(I18N.getString("message.preparing-scripts"));
+
+        Map<String, String> data = new HashMap<>();
+
+        data.put("DEPLOY_DAEMON_IDENTIFIER", getDaemonIdentifier(params));
+        data.put("DEPLOY_LAUNCHD_PLIST_FILE",
+                IDENTIFIER.fetchFrom(params).toLowerCase() + ".launchd.plist");
+
+        Writer w = new BufferedWriter(
+                new FileWriter(getScripts_PreinstallFile(params)));
+        String content = preprocessTextResource(
+                getScripts_PreinstallFile(params).getName(),
+                I18N.getString("resource.pkg-preinstall-script"),
+                TEMPLATE_PREINSTALL_SCRIPT,
+                data,
+                VERBOSE.fetchFrom(params),
+                RESOURCE_DIR.fetchFrom(params));
+        w.write(content);
+        w.close();
+        getScripts_PreinstallFile(params).setExecutable(true, false);
+
+        w = new BufferedWriter(
+                new FileWriter(getScripts_PostinstallFile(params)));
+        content = preprocessTextResource(
+                getScripts_PostinstallFile(params).getName(),
+                I18N.getString("resource.pkg-postinstall-script"),
+                TEMPLATE_POSTINSTALL_SCRIPT,
+                data,
+                VERBOSE.fetchFrom(params),
+                RESOURCE_DIR.fetchFrom(params));
+        w.write(content);
+        w.close();
+        getScripts_PostinstallFile(params).setExecutable(true, false);
+    }
+
+    private void prepareDistributionXMLFile(Map<String, ? super Object> params)
+            throws IOException {
+        File f = getConfig_DistributionXMLFile(params);
+
+        Log.verbose(MessageFormat.format(I18N.getString(
+                "message.preparing-distribution-dist"), f.getAbsolutePath()));
+
+        PrintStream out = new PrintStream(f);
+
+        out.println(
+                "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"no\"?>");
+        out.println("<installer-gui-script minSpecVersion=\"1\">");
+
+        out.println("<title>" + APP_NAME.fetchFrom(params) + "</title>");
+        out.println("<background" + " file=\""
+                + getConfig_BackgroundImage(params).getName()
+                + "\""
+                + " mime-type=\"image/png\""
+                + " alignment=\"bottomleft\" "
+                + " scaling=\"none\""
+                + "/>");
+
+        String licFileStr = LICENSE_FILE.fetchFrom(params);
+        if (licFileStr != null) {
+            File licFile = new File(licFileStr);
+            out.println("<license"
+                    + " file=\"" + licFile.getAbsolutePath() + "\""
+                    + " mime-type=\"text/rtf\""
+                    + "/>");
+        }
+
+        /*
+         * Note that the content of the distribution file
+         * below is generated by productbuild --synthesize
+         */
+
+        String appId = getAppIdentifier(params);
+
+        out.println("<pkg-ref id=\"" + appId + "\"/>");
+
+        out.println("<options customize=\"never\" require-scripts=\"false\"/>");
+        out.println("<choices-outline>");
+        out.println("    <line choice=\"default\">");
+        out.println("        <line choice=\"" + appId + "\"/>");
+        out.println("    </line>");
+        out.println("</choices-outline>");
+        out.println("<choice id=\"default\"/>");
+        out.println("<choice id=\"" + appId + "\" visible=\"false\">");
+        out.println("    <pkg-ref id=\"" + appId + "\"/>");
+        out.println("</choice>");
+        out.println("<pkg-ref id=\"" + appId + "\" version=\""
+                + VERSION.fetchFrom(params) + "\" onConclusion=\"none\">"
+                + URLEncoder.encode(getPackages_AppPackage(params).getName(),
+                "UTF-8") + "</pkg-ref>");
+
+        out.println("</installer-gui-script>");
+
+        out.close();
+    }
+
+    private boolean prepareConfigFiles(Map<String, ? super Object> params)
+            throws IOException {
+        File imageTarget = getConfig_BackgroundImage(params);
+        fetchResource(imageTarget.getName(),
+                I18N.getString("resource.pkg-background-image"),
+                DEFAULT_BACKGROUND_IMAGE,
+                imageTarget,
+                VERBOSE.fetchFrom(params),
+                RESOURCE_DIR.fetchFrom(params));
+
+        prepareDistributionXMLFile(params);
+
+        fetchResource(getConfig_Script(params).getName(),
+                I18N.getString("resource.post-install-script"),
+                (String) null,
+                getConfig_Script(params),
+                VERBOSE.fetchFrom(params),
+                RESOURCE_DIR.fetchFrom(params));
+
+        return true;
+    }
+
+    // name of post-image script
+    private File getConfig_Script(Map<String, ? super Object> params) {
+        return new File(CONFIG_ROOT.fetchFrom(params),
+                APP_NAME.fetchFrom(params) + "-post-image.sh");
+    }
+
+    private void patchCPLFile(File cpl) throws IOException {
+        String cplData = Files.readString(cpl.toPath());
+        String[] lines = cplData.split("\n");
+        try (PrintWriter out = new PrintWriter(new BufferedWriter(
+                new FileWriter(cpl)))) {
+            boolean skip = false; // Used to skip Java.runtime bundle, since
+            // pkgbuild with --root will find two bundles app and Java runtime.
+            // We cannot generate component proprty list when using
+            // --component argument.
+            for (int i = 0; i < lines.length; i++) {
+                if (lines[i].trim().equals("<key>BundleIsRelocatable</key>")) {
+                    out.println(lines[i]);
+                    out.println("<false/>");
+                    i++;
+                } else if (lines[i].trim().equals("<key>ChildBundles</key>")) {
+                    skip = true;
+                } else if (skip && lines[i].trim().equals("</array>")) {
+                    skip = false;
+                } else {
+                    if (!skip) {
+                        out.println(lines[i]);
+                    }
+                }
+            }
+        }
+    }
+
+    private File createPKG(Map<String, ? super Object> params,
+            File outdir, File appLocation) {
+        // generic find attempt
+        try {
+            File appPKG = getPackages_AppPackage(params);
+
+            // Generate default CPL file
+            File cpl = new File(CONFIG_ROOT.fetchFrom(params).getAbsolutePath()
+                    + File.separator + "cpl.plist");
+            ProcessBuilder pb = new ProcessBuilder("pkgbuild",
+                    "--root",
+                    appLocation.getParent(),
+                    "--install-location",
+                    MAC_INSTALL_DIR.fetchFrom(params),
+                    "--analyze",
+                    cpl.getAbsolutePath());
+
+            IOUtils.exec(pb, false);
+
+            patchCPLFile(cpl);
+
+            // build application package
+            pb = new ProcessBuilder("pkgbuild",
+                    "--root",
+                    appLocation.getParent(),
+                    "--install-location",
+                    MAC_INSTALL_DIR.fetchFrom(params),
+                    "--component-plist",
+                    cpl.getAbsolutePath(),
+                    appPKG.getAbsolutePath());
+            IOUtils.exec(pb, false);
+
+            // build final package
+            File finalPKG = new File(outdir, INSTALLER_NAME.fetchFrom(params)
+                    + INSTALLER_SUFFIX.fetchFrom(params)
+                    + ".pkg");
+            outdir.mkdirs();
+
+            List<String> commandLine = new ArrayList<>();
+            commandLine.add("productbuild");
+
+            commandLine.add("--resources");
+            commandLine.add(CONFIG_ROOT.fetchFrom(params).getAbsolutePath());
+
+            // maybe sign
+            if (Optional.ofNullable(MacAppImageBuilder.
+                    SIGN_BUNDLE.fetchFrom(params)).orElse(Boolean.TRUE)) {
+                if (Platform.getMajorVersion() > 10 ||
+                    (Platform.getMajorVersion() == 10 &&
+                    Platform.getMinorVersion() >= 12)) {
+                    // we need this for OS X 10.12+
+                    Log.verbose(I18N.getString("message.signing.pkg"));
+                }
+
+                String signingIdentity =
+                        DEVELOPER_ID_INSTALLER_SIGNING_KEY.fetchFrom(params);
+                if (signingIdentity != null) {
+                    commandLine.add("--sign");
+                    commandLine.add(signingIdentity);
+                }
+
+                String keychainName = SIGNING_KEYCHAIN.fetchFrom(params);
+                if (keychainName != null && !keychainName.isEmpty()) {
+                    commandLine.add("--keychain");
+                    commandLine.add(keychainName);
+                }
+            }
+
+            commandLine.add("--distribution");
+            commandLine.add(
+                    getConfig_DistributionXMLFile(params).getAbsolutePath());
+            commandLine.add("--package-path");
+            commandLine.add(PACKAGES_ROOT.fetchFrom(params).getAbsolutePath());
+
+            commandLine.add(finalPKG.getAbsolutePath());
+
+            pb = new ProcessBuilder(commandLine);
+            IOUtils.exec(pb, false);
+
+            return finalPKG;
+        } catch (Exception ignored) {
+            Log.verbose(ignored);
+            return null;
+        }
+    }
+
+    //////////////////////////////////////////////////////////////////////////
+    // Implement Bundler
+    //////////////////////////////////////////////////////////////////////////
+
+    @Override
+    public String getName() {
+        return I18N.getString("pkg.bundler.name");
+    }
+
+    @Override
+    public String getDescription() {
+        return I18N.getString("pkg.bundler.description");
+    }
+
+    @Override
+    public String getID() {
+        return "pkg";
+    }
+
+    @Override
+    public Collection<BundlerParamInfo<?>> getBundleParameters() {
+        Collection<BundlerParamInfo<?>> results = new LinkedHashSet<>();
+        results.addAll(MacAppBundler.getAppBundleParameters());
+        results.addAll(getPKGBundleParameters());
+        return results;
+    }
+
+    public Collection<BundlerParamInfo<?>> getPKGBundleParameters() {
+        Collection<BundlerParamInfo<?>> results = new LinkedHashSet<>();
+
+        results.addAll(MacAppBundler.getAppBundleParameters());
+        results.addAll(Arrays.asList(
+                DEVELOPER_ID_INSTALLER_SIGNING_KEY,
+                // IDENTIFIER,
+                INSTALLER_SUFFIX,
+                LICENSE_FILE,
+                // SERVICE_HINT,
+                SIGNING_KEYCHAIN));
+
+        return results;
+    }
+
+    @Override
+    public boolean validate(Map<String, ? super Object> params)
+            throws UnsupportedPlatformException, ConfigException {
+        try {
+            if (params == null) throw new ConfigException(
+                    I18N.getString("error.parameters-null"),
+                    I18N.getString("error.parameters-null.advice"));
+
+            // run basic validation to ensure requirements are met
+            // we are not interested in return code, only possible exception
+            validateAppImageAndBundeler(params);
+
+            // reject explicitly set sign to true and no valid signature key
+            if (Optional.ofNullable(MacAppImageBuilder.
+                    SIGN_BUNDLE.fetchFrom(params)).orElse(Boolean.FALSE)) {
+                String signingIdentity =
+                        DEVELOPER_ID_INSTALLER_SIGNING_KEY.fetchFrom(params);
+                if (signingIdentity == null) {
+                    throw new ConfigException(
+                            I18N.getString("error.explicit-sign-no-cert"),
+                            I18N.getString(
+                            "error.explicit-sign-no-cert.advice"));
+                }
+            }
+
+            // hdiutil is always available so there's no need
+            // to test for availability.
+
+            return true;
+        } catch (RuntimeException re) {
+            if (re.getCause() instanceof ConfigException) {
+                throw (ConfigException) re.getCause();
+            } else {
+                throw new ConfigException(re);
+            }
+        }
+    }
+
+    @Override
+    public File execute(Map<String, ? super Object> params,
+            File outputParentDir) throws PackagerException {
+        return bundle(params, outputParentDir);
+    }
+
+    @Override
+    public boolean supported(boolean runtimeInstaller) {
+        return Platform.getPlatform() == Platform.MAC;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/DMGsetup.scpt	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,15 @@
+tell application "Finder"
+  tell disk "DEPLOY_ACTUAL_VOLUME_NAME"
+    open
+    set current view of container window to icon view
+    set toolbar visible of container window to false
+    set statusbar visible of container window to false
+
+    set the bounds of container window to {400, 100, 917, 370}
+
+    set theViewOptions to the icon view options of container window
+    set arrangement of theViewOptions to not arranged
+    set icon size of theViewOptions to 128
+  end tell
+end tell
+
Binary file src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/GenericApp.icns has changed
Binary file src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/GenericAppHiDPI.icns has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/Info-lite.plist.template	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,38 @@
+<?xml version="1.0" ?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+ <dict>
+  <key>LSMinimumSystemVersion</key>
+  <string>10.9</string>
+  <key>CFBundleDevelopmentRegion</key>
+  <string>English</string>
+  <key>CFBundleAllowMixedLocalizations</key>
+  <true/>
+  <key>CFBundleExecutable</key>
+  <string>DEPLOY_LAUNCHER_NAME</string>
+  <key>CFBundleIconFile</key>
+  <string>DEPLOY_ICON_FILE</string>
+  <key>CFBundleIdentifier</key>
+  <string>DEPLOY_BUNDLE_IDENTIFIER</string>
+  <key>CFBundleInfoDictionaryVersion</key>
+  <string>6.0</string>
+  <key>CFBundleName</key>
+  <string>DEPLOY_BUNDLE_NAME</string>
+  <key>CFBundlePackageType</key>
+  <string>APPL</string>
+  <key>CFBundleShortVersionString</key>
+  <string>DEPLOY_BUNDLE_SHORT_VERSION</string>
+  <key>CFBundleSignature</key>
+  <string>????</string>
+  <!-- See http://developer.apple.com/library/mac/#releasenotes/General/SubmittingToMacAppStore/_index.html
+       for list of AppStore categories -->
+  <key>LSApplicationCategoryType</key>
+  <string>DEPLOY_BUNDLE_CATEGORY</string>
+  <key>CFBundleVersion</key>
+  <string>DEPLOY_BUNDLE_CFBUNDLE_VERSION</string>
+  <key>NSHumanReadableCopyright</key>
+  <string>DEPLOY_BUNDLE_COPYRIGHT</string>DEPLOY_FILE_ASSOCIATIONS
+  <key>NSHighResolutionCapable</key>
+  <string>true</string>
+ </dict>
+</plist>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/Info.plist.template	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,56 @@
+<?xml version="1.0" ?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+ <dict>
+  <key>LSMinimumSystemVersion</key>
+  <string>10.7.4</string>
+  <key>CFBundleDevelopmentRegion</key>
+  <string>English</string>
+  <key>CFBundleAllowMixedLocalizations</key>
+  <true/>
+  <key>CFBundleExecutable</key>
+  <string>DEPLOY_LAUNCHER_NAME</string>
+  <key>CFBundleIconFile</key>
+  <string>DEPLOY_ICON_FILE</string>
+  <key>CFBundleIdentifier</key>
+  <string>DEPLOY_BUNDLE_IDENTIFIER</string>
+  <key>CFBundleInfoDictionaryVersion</key>
+  <string>6.0</string>
+  <key>CFBundleName</key>
+  <string>DEPLOY_BUNDLE_NAME</string>
+  <key>CFBundlePackageType</key>
+  <string>APPL</string>
+  <key>CFBundleShortVersionString</key>
+  <string>DEPLOY_BUNDLE_SHORT_VERSION</string>
+  <key>CFBundleSignature</key>
+  <string>????</string>
+  <!-- See http://developer.apple.com/library/mac/#releasenotes/General/SubmittingToMacAppStore/_index.html
+       for list of AppStore categories -->
+  <key>LSApplicationCategoryType</key>
+  <string>DEPLOY_BUNDLE_CATEGORY</string>
+  <key>CFBundleVersion</key>
+  <string>DEPLOY_BUNDLE_CFBUNDLE_VERSION</string>
+  <key>NSHumanReadableCopyright</key>
+  <string>DEPLOY_BUNDLE_COPYRIGHT</string>
+  <key>JVMRuntime</key>
+  <string>DEPLOY_JAVA_RUNTIME_NAME</string>
+  <key>JVMMainClassName</key>
+  <string>DEPLOY_LAUNCHER_CLASS</string>
+  <key>JVMAppClasspath</key>
+  <string>DEPLOY_APP_CLASSPATH</string>
+  <key>JVMMainJarName</key>
+  <string>DEPLOY_MAIN_JAR_NAME</string>
+  <key>JVMPreferencesID</key>
+  <string>DEPLOY_PREFERENCES_ID</string>
+  <key>JVMOptions</key>
+  <array>
+DEPLOY_JVM_OPTIONS
+  </array>
+  <key>ArgOptions</key>
+  <array>
+DEPLOY_ARGUMENTS
+  </array>DEPLOY_FILE_ASSOCIATIONS
+  <key>NSHighResolutionCapable</key>
+  <string>true</string>
+ </dict>
+</plist>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacAppStore.entitlements	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+    <dict>
+        <key>com.apple.security.app-sandbox</key>
+        <true/>
+    </dict>
+</plist>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacAppStore_Inherit.entitlements	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+    <dict>
+        <key>com.apple.security.app-sandbox</key>
+        <true/>
+        <key>com.apple.security.inherit</key>
+        <true/>
+    </dict>
+</plist>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources.properties	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,160 @@
+#
+# Copyright (c) 2017, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+# particular file as subject to the "Classpath" exception as provided
+# by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+# or visit www.oracle.com if you need additional information or have any
+# questions.
+#
+#
+
+app.bundler.name=Mac Application Image
+app.bundler.description=A Directory based image of a mac Application with an optionally co-bundled JRE.  Used as a base for the Installer bundlers
+store.bundler.name=Mac App Store Ready Bundler
+store.bundler.description=Creates a binary bundle ready for deployment into the Mac App Store."
+dmg.bundler.name=DMG Installer
+dmg.bundler.description=Mac DMG Installer Bundle
+pkg.bundler.name=PKG Installer
+pkg.bundler.description=Mac PKG Installer Bundle.
+
+param.signing-key-developer-id-app.name=Apple Developer ID Application Signing Key
+param.signing-key-developer-id-app.description=The full name of the Apple Developer ID Application signing key.
+param.icon-icns.name=.icns Icon
+param.icon-icns.description=Icon for the application, in ICNS format.
+param.configure-launcher-in-plist=Configure Launcher in Info.plist
+param.configure-launcher-in-plist.description=Should the legacy method of configuring hte launcher in the Info.plist be used.
+param.category-name=Category
+param.category-name.description=Mac App Store Categories. Note that the key is the string to display to the user and the value is the id of the category.
+param.cfbundle-name.name=CFBundleName
+param.cfbundle-name.description=The name of the app as it appears in the Menu Bar.  This can be different from the application name.  This name should be less than 16 characters long and be suitable for displaying in the menu bar and the app's Info window.
+param.cfbundle-identifier.name=CFBundleIdentifier
+param.cfbundle-identifier.description=An identifier that uniquely identifies the application for MacOSX (and on the Mac App Store).  May only use alphanumeric (A-Z,a-z,0-9), hyphen (-), and period (.) characters.
+param.cfbundle-version.name=CFBundleVersion
+param.cfbundle-version.description=An computer readable version for the CFBundle.  May contain only digits and from zero to two dots, such as "1.8.1" or "100".
+param.bundle-id-signing-prefix.name=Bundle Signing Prefix
+param.bundle-id-signing-prefix.description=When signing the application bundle this value is prefixed to all components that need to be signed that don't have an existing CFBundleIdentifier.
+param.raw-executable-url.name=Launcher URL
+param.raw-executable-url.description=Override the jpackage default launcher with a custom launcher.
+param.default-icon-icns=Default Icon
+param.default-icon-icns.description=The Default Icon for when a user does not specify an icns file.
+param.icon-icns.name=.icns Icon
+param.icon-icns.description= Icon for the application, in ICNS format.
+param.sign-bundle.name=Sign Bundle
+param.sign-bundle.description=Request that the bundle be signed.
+param.signing-key-app.name=Application Signing Key
+param.signing-key-app.description=The full name of the signing key to sign the application with.
+param.signing-key-pkg.name=Installer Signing Key
+param.signing-key-pkg.description=The full name of the signing key to sign the PKG Installer with.
+param.mac-app-store-entitlements.name=Entitlements File
+param.mac-app-store-entitlements.description=File location of a custom mac app store entitlements file
+param.installer-suffix.name=Installer Suffix
+param.installer-suffix.description=The suffix for the installer this package.
+param.app-bundler.name=Mac App Bundler
+param.app-bundle.description=Creates a .app bundle for the Mac
+param.app-image-build-root.name=Build Root Dir
+param.app-image-build-root.description=This is temporary location built by jpackage that is the root of the image application
+param.signing-keychain.name=Signing Keychain
+param.signing-keychain.description=The location of the keychain to use.  If not specified the standard keychains will be used.
+param.signing-key-name.name=Signing Key User Name
+param.signing-key-name.description=The user name portion of the typical "Mac Developer ID Application: <user name>" signing key.
+param.installer-name.name=Installer Name
+param.installer-name.description=The filename of the generated installer without the file type extension.  Default is <App Name>-<Version>.
+param.simple-dmg.name=Simple DMG Generation
+param.simple-dmg.description=Generate a DMG without AppleScript customizations.  Recommended for continuous automated builds.
+param.signing-key-developer-id-installer.name=Apple Developer ID Installer Signing Key
+param.signing-key-developer-id-installer.description=The full name of the Apple Developer ID Installer signing key.
+param.packages-root.name=PKG Root Dir
+param.packages-root.description=This is temporary location for component packages (application and daemon).  The packages are incorporated into final product package.
+param.scripts-dir.name=Scripts Dir
+param.scripts-dir.description=This is temporary location for package scripts
+param.mac-install-dir.name=Mac Installation Directory
+param.mac-install-dir.description=Installation directory of the application on Mac.
+
+error.invalid-cfbundle-version=Invalid CFBundleVersion - ''{0}''
+error.invalid-cfbundle-version.advice=Set a compatible 'appVersion' or set a 'mac.CFBundleVersion'.  Valid versions are one to three integers separated by dots.
+error.explicit-sign-no-cert=Signature explicitly requested but no signing certificate specified.
+error.explicit-sign-no-cert.advice=Either specify a valid cert in 'mac.signing-key-developer-id-app' or unset 'signBundle' or set 'signBundle' to false.
+error.non-existent-runtime=The file for the Runtime/JRE directory does not exist.
+error.non-existent-runtime.advice=Point the runtime parameter to a directory that containes the JRE.
+error.cannot-detect-runtime-in-directory=Cannot determine which JRE/JDK exists in the specified runtime directory.
+error.cannot-detect-runtime-in-directory.advice=Point the runtime directory to one of the JDK/JRE root, the Contents/Home directory of that root, or the Contents/Home/jre directory of the JDK.
+error.parameters-null=Parameters map is null.
+error.parameters-null.advice=Pass in a non-null parameters map.
+error.cannot-create-output-dir=Output directory {0} cannot be created.
+error.cannot-write-to-output-dir=Output directory {0} is not writable.
+error.no-system-runtime=Bundle Configured to use the System JRE
+error.no-system-runtime.advice=Do not set 'runtime' to null, either don't set it or set it to a valid value.
+error.must-sign-app-store=Mac App Store apps must be signed, and signing has been disabled by bundler configuration.
+error.must-sign-app-store.advice=Either unset 'signBundle' or set 'signBundle' to true.
+error.no-app-signing-key=No Mac App Store App Signing Key
+error.no-app-signing-key.advice=Install your app signing keys into your Mac Keychain using XCode.
+error.no-pkg-signing-key=No Mac App Store Installer Signing Key
+error.no-pkg-signing-key.advice=Install your app signing keys into your Mac Keychain using XCode.
+error.certificate.expired=Error: Certificate expired {0}.
+error.dmg-does-not-do-daemons=DMG bundler doesn't support services.
+error.dmg-does-not-do-daemons.advice=Make sure that the service hint is set to false.
+
+resource.bundle-config-file=Bundle config file
+resource.app-info-plist=Application Info.plist
+resource.runtime-info-plist=Java Runtime Info.plist
+resource.mac-app-store-entitlements=Mac App Store Entitlements
+resource.mac-app-store-inherit-entitlements=Mac App Store Inherit Entitlements
+resource.dmg-setup-script=DMG setup script
+resource.license-setup=License setup
+resource.dmg-background=dmg background
+resource.volume-icon=volume icon
+resource.post-install-script=script to run after application image is populated
+resource.pkg-preinstall-script=PKG preinstall script
+resource.pkg-postinstall-script=PKG postinstall script
+resource.pkg-background-image=pkg background image
+
+
+message.bundle-name-too-long-warning={0} is set to ''{1}'', which is longer than 16 characters.  For a better Mac experience consider shortening it.
+message.no-mac-jre-support=Currently Macs require a JDK to package
+message.null-classpath=Null app resources?
+message.preparing-info-plist=Preparing Info.plist\: {0}
+message.icon-not-icns= The specified icon "{0}" is not an ICNS file and will not be used.  The default icon will be used in it's place.
+message.version-string-too-many-components=Version sting may have between 1 and 3 numbers: 1, 1.2, 1.2.3.
+message.version-string-first-number-not-zero=The first number in a CFBundleVersion cannot be zero or negative.
+message.version-string-no-negative-numbers=Negative numbers are not allowed in version strings.
+message.version-string-numbers-only=Version strings can consist of only numbers and up to two dots.
+message.creating-association-with-null-extension=Creating association with null extension.
+message.ignoring.symlink=Warning: codesign is skipping the symlink {0}
+message.keychain.error=Error: unable to get keychain list.
+message.building-bundle=Building Mac App Store Bundle for {0}
+mesasge.intermediate-bundle-location=Intermediate application bundle image\: {0}
+message.app-image-dir-does-not-exist=Specified application image directory {0}\: {1} does not exists
+message.app-image-dir-does-not-exist.advice=Confirm that the value for {0} exists
+message.could-not-retrieve-name=Could not retrieve gecos name
+message.app-image-requires-app-name=When using an external app image you must specify the app name.
+message.app-image-requires-app-name.advice=Set the app name via the -name CLI flag, the fx:application/@name ANT attribute, or via the 'appName' bundler argument.
+message.app-image-requires-identifier=When using an external app image you must specify the identifier.
+message.app-image-requires-identifier.advice=Set the identifier via the -appId CLI flag, the fx:application/@id ANT attribute, or via the 'identifier' bundler argument.
+message.building-dmg=Building DMG package for {0}
+message.running-script=Running shell script on application image [{0}]
+message.intermediate-image-location=[DEBUG] Intermediate application bundle image\: {0}
+message.preparing-dmg-setup=Preparing dmg setup\: {0}
+message.creating-dmg-file=Creating DMG file\: {0}
+message.dmg-cannot-be-overwritten=Dmg file exists ({0} and can not be removed.
+message.output-to-location=Result DMG installer for {0}\: {1}
+message.building-pkg=Building PKG package for {0}
+message.preparing-scripts=Preparing package scripts
+message.preparing-distribution-dist=Preparing distribution.dist\: {0}
+message.signing.pkg=Warning: For signing PKG, you might need to set "Always Trust" for your certificate using "Keychain Access" tool.
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_ja.properties	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,160 @@
+#
+# Copyright (c) 2017, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+# particular file as subject to the "Classpath" exception as provided
+# by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+# or visit www.oracle.com if you need additional information or have any
+# questions.
+#
+#
+
+app.bundler.name=Mac Application Image
+app.bundler.description=A Directory based image of a mac Application with an optionally co-bundled JRE.  Used as a base for the Installer bundlers
+store.bundler.name=Mac App Store Ready Bundler
+store.bundler.description=Creates a binary bundle ready for deployment into the Mac App Store."
+dmg.bundler.name=DMG Installer
+dmg.bundler.description=Mac DMG Installer Bundle
+pkg.bundler.name=PKG Installer
+pkg.bundler.description=Mac PKG Installer Bundle.
+
+param.signing-key-developer-id-app.name=Apple Developer ID Application Signing Key
+param.signing-key-developer-id-app.description=The full name of the Apple Developer ID Application signing key.
+param.icon-icns.name=.icns Icon
+param.icon-icns.description=Icon for the application, in ICNS format.
+param.configure-launcher-in-plist=Configure Launcher in Info.plist
+param.configure-launcher-in-plist.description=Should the legacy method of configuring hte launcher in the Info.plist be used.
+param.category-name=Category
+param.category-name.description=Mac App Store Categories. Note that the key is the string to display to the user and the value is the id of the category.
+param.cfbundle-name.name=CFBundleName
+param.cfbundle-name.description=The name of the app as it appears in the Menu Bar.  This can be different from the application name.  This name should be less than 16 characters long and be suitable for displaying in the menu bar and the app's Info window.
+param.cfbundle-identifier.name=CFBundleIdentifier
+param.cfbundle-identifier.description=An identifier that uniquely identifies the application for MacOSX (and on the Mac App Store).  May only use alphanumeric (A-Z,a-z,0-9), hyphen (-), and period (.) characters.
+param.cfbundle-version.name=CFBundleVersion
+param.cfbundle-version.description=An computer readable version for the CFBundle.  May contain only digits and from zero to two dots, such as "1.8.1" or "100".
+param.bundle-id-signing-prefix.name=Bundle Signing Prefix
+param.bundle-id-signing-prefix.description=When signing the application bundle this value is prefixed to all components that need to be signed that don't have an existing CFBundleIdentifier.
+param.raw-executable-url.name=Launcher URL
+param.raw-executable-url.description=Override the jpackage default launcher with a custom launcher.
+param.default-icon-icns=Default Icon
+param.default-icon-icns.description=The Default Icon for when a user does not specify an icns file.
+param.icon-icns.name=.icns Icon
+param.icon-icns.description= Icon for the application, in ICNS format.
+param.sign-bundle.name=Sign Bundle
+param.sign-bundle.description=Request that the bundle be signed.
+param.signing-key-app.name=Application Signing Key
+param.signing-key-app.description=The full name of the signing key to sign the application with.
+param.signing-key-pkg.name=Installer Signing Key
+param.signing-key-pkg.description=The full name of the signing key to sign the PKG Installer with.
+param.mac-app-store-entitlements.name=Entitlements File
+param.mac-app-store-entitlements.description=File location of a custom mac app store entitlements file
+param.installer-suffix.name=Installer Suffix
+param.installer-suffix.description=The suffix for the installer this package.
+param.app-bundler.name=Mac App Bundler
+param.app-bundle.description=Creates a .app bundle for the Mac
+param.app-image-build-root.name=Build Root Dir
+param.app-image-build-root.description=This is temporary location built by jpackage that is the root of the image application
+param.signing-keychain.name=Signing Keychain
+param.signing-keychain.description=The location of the keychain to use.  If not specified the standard keychains will be used.
+param.signing-key-name.name=Signing Key User Name
+param.signing-key-name.description=The user name portion of the typical "Mac Developer ID Application: <user name>" signing key.
+param.installer-name.name=Installer Name
+param.installer-name.description=The filename of the generated installer without the file type extension.  Default is <App Name>-<Version>.
+param.simple-dmg.name=Simple DMG Generation
+param.simple-dmg.description=Generate a DMG without AppleScript customizations.  Recommended for continuous automated builds.
+param.signing-key-developer-id-installer.name=Apple Developer ID Installer Signing Key
+param.signing-key-developer-id-installer.description=The full name of the Apple Developer ID Installer signing key.
+param.packages-root.name=PKG Root Dir
+param.packages-root.description=This is temporary location for component packages (application and daemon).  The packages are incorporated into final product package.
+param.scripts-dir.name=Scripts Dir
+param.scripts-dir.description=This is temporary location for package scripts
+param.mac-install-dir.name=Mac Installation Directory
+param.mac-install-dir.description=Installation directory of the application on Mac.
+
+error.invalid-cfbundle-version=Invalid CFBundleVersion - ''{0}''
+error.invalid-cfbundle-version.advice=Set a compatible 'appVersion' or set a 'mac.CFBundleVersion'.  Valid versions are one to three integers separated by dots.
+error.explicit-sign-no-cert=Signature explicitly requested but no signing certificate specified.
+error.explicit-sign-no-cert.advice=Either specify a valid cert in 'mac.signing-key-developer-id-app' or unset 'signBundle' or set 'signBundle' to false.
+error.non-existent-runtime=The file for the Runtime/JRE directory does not exist.
+error.non-existent-runtime.advice=Point the runtime parameter to a directory that containes the JRE.
+error.cannot-detect-runtime-in-directory=Cannot determine which JRE/JDK exists in the specified runtime directory.
+error.cannot-detect-runtime-in-directory.advice=Point the runtime directory to one of the JDK/JRE root, the Contents/Home directory of that root, or the Contents/Home/jre directory of the JDK.
+error.parameters-null=Parameters map is null.
+error.parameters-null.advice=Pass in a non-null parameters map.
+error.cannot-create-output-dir=Output directory {0} cannot be created.
+error.cannot-write-to-output-dir=Output directory {0} is not writable.
+error.no-system-runtime=Bundle Configured to use the System JRE
+error.no-system-runtime.advice=Do not set 'runtime' to null, either don't set it or set it to a valid value.
+error.must-sign-app-store=Mac App Store apps must be signed, and signing has been disabled by bundler configuration.
+error.must-sign-app-store.advice=Either unset 'signBundle' or set 'signBundle' to true.
+error.no-app-signing-key=No Mac App Store App Signing Key
+error.no-app-signing-key.advice=Install your app signing keys into your Mac Keychain using XCode.
+error.no-pkg-signing-key=No Mac App Store Installer Signing Key
+error.no-pkg-signing-key.advice=Install your app signing keys into your Mac Keychain using XCode.
+error.certificate.expired=Error: Certificate expired {0}.
+error.dmg-does-not-do-daemons=DMG bundler doesn't support services.
+error.dmg-does-not-do-daemons.advice=Make sure that the service hint is set to false.
+
+resource.bundle-config-file=Bundle config file
+resource.app-info-plist=Application Info.plist
+resource.runtime-info-plist=Java Runtime Info.plist
+resource.mac-app-store-entitlements=Mac App Store Entitlements
+resource.mac-app-store-inherit-entitlements=Mac App Store Inherit Entitlements
+resource.dmg-setup-script=DMG setup script
+resource.license-setup=License setup
+resource.dmg-background=dmg background
+resource.volume-icon=volume icon
+resource.post-install-script=script to run after application image is populated
+resource.pkg-preinstall-script=PKG preinstall script
+resource.pkg-postinstall-script=PKG postinstall script
+resource.pkg-background-image=pkg background image
+
+
+message.bundle-name-too-long-warning={0} is set to ''{1}'', which is longer than 16 characters.  For a better Mac experience consider shortening it.
+message.no-mac-jre-support=Currently Macs require a JDK to package
+message.null-classpath=Null app resources?
+message.preparing-info-plist=Preparing Info.plist\: {0}
+message.icon-not-icns= The specified icon "{0}" is not an ICNS file and will not be used.  The default icon will be used in it's place.
+message.version-string-too-many-components=Version sting may have between 1 and 3 numbers: 1, 1.2, 1.2.3.
+message.version-string-first-number-not-zero=The first number in a CFBundleVersion cannot be zero or negative.
+message.version-string-no-negative-numbers=Negative numbers are not allowed in version strings.
+message.version-string-numbers-only=Version strings can consist of only numbers and up to two dots.
+message.creating-association-with-null-extension=Creating association with null extension.
+message.ignoring.symlink=Warning: codesign is skipping the symlink {0}
+message.keychain.error=Error: unable to get keychain list.
+message.building-bundle=Building Mac App Store Bundle for {0}
+mesasge.intermediate-bundle-location=Intermediate application bundle image\: {0}
+message.app-image-dir-does-not-exist=Specified application image directory {0}\: {1} does not exists
+message.app-image-dir-does-not-exist.advice=Confirm that the value for {0} exists
+message.could-not-retrieve-name=Could not retrieve gecos name
+message.app-image-requires-app-name=When using an external app image you must specify the app name.
+message.app-image-requires-app-name.advice=Set the app name via the -name CLI flag, the fx:application/@name ANT attribute, or via the 'appName' bundler argument.
+message.app-image-requires-identifier=When using an external app image you must specify the identifier.
+message.app-image-requires-identifier.advice=Set the identifier via the -appId CLI flag, the fx:application/@id ANT attribute, or via the 'identifier' bundler argument.
+message.building-dmg=Building DMG package for {0}
+message.running-script=Running shell script on application image [{0}]
+message.intermediate-image-location=[DEBUG] Intermediate application bundle image\: {0}
+message.preparing-dmg-setup=Preparing dmg setup\: {0}
+message.creating-dmg-file=Creating DMG file\: {0}
+message.dmg-cannot-be-overwritten=Dmg file exists ({0} and can not be removed.
+message.output-to-location=Result DMG installer for {0}\: {1}
+message.building-pkg=Building PKG package for {0}
+message.preparing-scripts=Preparing package scripts
+message.preparing-distribution-dist=Preparing distribution.dist\: {0}
+message.signing.pkg=Warning: For signing PKG, you might need to set "Always Trust" for your certificate using "Keychain Access" tool.
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_zh_CN.properties	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,160 @@
+#
+# Copyright (c) 2017, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+# particular file as subject to the "Classpath" exception as provided
+# by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+# or visit www.oracle.com if you need additional information or have any
+# questions.
+#
+#
+
+app.bundler.name=Mac Application Image
+app.bundler.description=A Directory based image of a mac Application with an optionally co-bundled JRE.  Used as a base for the Installer bundlers
+store.bundler.name=Mac App Store Ready Bundler
+store.bundler.description=Creates a binary bundle ready for deployment into the Mac App Store."
+dmg.bundler.name=DMG Installer
+dmg.bundler.description=Mac DMG Installer Bundle
+pkg.bundler.name=PKG Installer
+pkg.bundler.description=Mac PKG Installer Bundle.
+
+param.signing-key-developer-id-app.name=Apple Developer ID Application Signing Key
+param.signing-key-developer-id-app.description=The full name of the Apple Developer ID Application signing key.
+param.icon-icns.name=.icns Icon
+param.icon-icns.description=Icon for the application, in ICNS format.
+param.configure-launcher-in-plist=Configure Launcher in Info.plist
+param.configure-launcher-in-plist.description=Should the legacy method of configuring hte launcher in the Info.plist be used.
+param.category-name=Category
+param.category-name.description=Mac App Store Categories. Note that the key is the string to display to the user and the value is the id of the category.
+param.cfbundle-name.name=CFBundleName
+param.cfbundle-name.description=The name of the app as it appears in the Menu Bar.  This can be different from the application name.  This name should be less than 16 characters long and be suitable for displaying in the menu bar and the app's Info window.
+param.cfbundle-identifier.name=CFBundleIdentifier
+param.cfbundle-identifier.description=An identifier that uniquely identifies the application for MacOSX (and on the Mac App Store).  May only use alphanumeric (A-Z,a-z,0-9), hyphen (-), and period (.) characters.
+param.cfbundle-version.name=CFBundleVersion
+param.cfbundle-version.description=An computer readable version for the CFBundle.  May contain only digits and from zero to two dots, such as "1.8.1" or "100".
+param.bundle-id-signing-prefix.name=Bundle Signing Prefix
+param.bundle-id-signing-prefix.description=When signing the application bundle this value is prefixed to all components that need to be signed that don't have an existing CFBundleIdentifier.
+param.raw-executable-url.name=Launcher URL
+param.raw-executable-url.description=Override the jpackage default launcher with a custom launcher.
+param.default-icon-icns=Default Icon
+param.default-icon-icns.description=The Default Icon for when a user does not specify an icns file.
+param.icon-icns.name=.icns Icon
+param.icon-icns.description= Icon for the application, in ICNS format.
+param.sign-bundle.name=Sign Bundle
+param.sign-bundle.description=Request that the bundle be signed.
+param.signing-key-app.name=Application Signing Key
+param.signing-key-app.description=The full name of the signing key to sign the application with.
+param.signing-key-pkg.name=Installer Signing Key
+param.signing-key-pkg.description=The full name of the signing key to sign the PKG Installer with.
+param.mac-app-store-entitlements.name=Entitlements File
+param.mac-app-store-entitlements.description=File location of a custom mac app store entitlements file
+param.installer-suffix.name=Installer Suffix
+param.installer-suffix.description=The suffix for the installer this package.
+param.app-bundler.name=Mac App Bundler
+param.app-bundle.description=Creates a .app bundle for the Mac
+param.app-image-build-root.name=Build Root Dir
+param.app-image-build-root.description=This is temporary location built by jpackage that is the root of the image application
+param.signing-keychain.name=Signing Keychain
+param.signing-keychain.description=The location of the keychain to use.  If not specified the standard keychains will be used.
+param.signing-key-name.name=Signing Key User Name
+param.signing-key-name.description=The user name portion of the typical "Mac Developer ID Application: <user name>" signing key.
+param.installer-name.name=Installer Name
+param.installer-name.description=The filename of the generated installer without the file type extension.  Default is <App Name>-<Version>.
+param.simple-dmg.name=Simple DMG Generation
+param.simple-dmg.description=Generate a DMG without AppleScript customizations.  Recommended for continuous automated builds.
+param.signing-key-developer-id-installer.name=Apple Developer ID Installer Signing Key
+param.signing-key-developer-id-installer.description=The full name of the Apple Developer ID Installer signing key.
+param.packages-root.name=PKG Root Dir
+param.packages-root.description=This is temporary location for component packages (application and daemon).  The packages are incorporated into final product package.
+param.scripts-dir.name=Scripts Dir
+param.scripts-dir.description=This is temporary location for package scripts
+param.mac-install-dir.name=Mac Installation Directory
+param.mac-install-dir.description=Installation directory of the application on Mac.
+
+error.invalid-cfbundle-version=Invalid CFBundleVersion - ''{0}''
+error.invalid-cfbundle-version.advice=Set a compatible 'appVersion' or set a 'mac.CFBundleVersion'.  Valid versions are one to three integers separated by dots.
+error.explicit-sign-no-cert=Signature explicitly requested but no signing certificate specified.
+error.explicit-sign-no-cert.advice=Either specify a valid cert in 'mac.signing-key-developer-id-app' or unset 'signBundle' or set 'signBundle' to false.
+error.non-existent-runtime=The file for the Runtime/JRE directory does not exist.
+error.non-existent-runtime.advice=Point the runtime parameter to a directory that containes the JRE.
+error.cannot-detect-runtime-in-directory=Cannot determine which JRE/JDK exists in the specified runtime directory.
+error.cannot-detect-runtime-in-directory.advice=Point the runtime directory to one of the JDK/JRE root, the Contents/Home directory of that root, or the Contents/Home/jre directory of the JDK.
+error.parameters-null=Parameters map is null.
+error.parameters-null.advice=Pass in a non-null parameters map.
+error.cannot-create-output-dir=Output directory {0} cannot be created.
+error.cannot-write-to-output-dir=Output directory {0} is not writable.
+error.no-system-runtime=Bundle Configured to use the System JRE
+error.no-system-runtime.advice=Do not set 'runtime' to null, either don't set it or set it to a valid value.
+error.must-sign-app-store=Mac App Store apps must be signed, and signing has been disabled by bundler configuration.
+error.must-sign-app-store.advice=Either unset 'signBundle' or set 'signBundle' to true.
+error.no-app-signing-key=No Mac App Store App Signing Key
+error.no-app-signing-key.advice=Install your app signing keys into your Mac Keychain using XCode.
+error.no-pkg-signing-key=No Mac App Store Installer Signing Key
+error.no-pkg-signing-key.advice=Install your app signing keys into your Mac Keychain using XCode.
+error.certificate.expired=Error: Certificate expired {0}.
+error.dmg-does-not-do-daemons=DMG bundler doesn't support services.
+error.dmg-does-not-do-daemons.advice=Make sure that the service hint is set to false.
+
+resource.bundle-config-file=Bundle config file
+resource.app-info-plist=Application Info.plist
+resource.runtime-info-plist=Java Runtime Info.plist
+resource.mac-app-store-entitlements=Mac App Store Entitlements
+resource.mac-app-store-inherit-entitlements=Mac App Store Inherit Entitlements
+resource.dmg-setup-script=DMG setup script
+resource.license-setup=License setup
+resource.dmg-background=dmg background
+resource.volume-icon=volume icon
+resource.post-install-script=script to run after application image is populated
+resource.pkg-preinstall-script=PKG preinstall script
+resource.pkg-postinstall-script=PKG postinstall script
+resource.pkg-background-image=pkg background image
+
+
+message.bundle-name-too-long-warning={0} is set to ''{1}'', which is longer than 16 characters.  For a better Mac experience consider shortening it.
+message.no-mac-jre-support=Currently Macs require a JDK to package
+message.null-classpath=Null app resources?
+message.preparing-info-plist=Preparing Info.plist\: {0}
+message.icon-not-icns= The specified icon "{0}" is not an ICNS file and will not be used.  The default icon will be used in it's place.
+message.version-string-too-many-components=Version sting may have between 1 and 3 numbers: 1, 1.2, 1.2.3.
+message.version-string-first-number-not-zero=The first number in a CFBundleVersion cannot be zero or negative.
+message.version-string-no-negative-numbers=Negative numbers are not allowed in version strings.
+message.version-string-numbers-only=Version strings can consist of only numbers and up to two dots.
+message.creating-association-with-null-extension=Creating association with null extension.
+message.ignoring.symlink=Warning: codesign is skipping the symlink {0}
+message.keychain.error=Error: unable to get keychain list.
+message.building-bundle=Building Mac App Store Bundle for {0}
+mesasge.intermediate-bundle-location=Intermediate application bundle image\: {0}
+message.app-image-dir-does-not-exist=Specified application image directory {0}\: {1} does not exists
+message.app-image-dir-does-not-exist.advice=Confirm that the value for {0} exists
+message.could-not-retrieve-name=Could not retrieve gecos name
+message.app-image-requires-app-name=When using an external app image you must specify the app name.
+message.app-image-requires-app-name.advice=Set the app name via the -name CLI flag, the fx:application/@name ANT attribute, or via the 'appName' bundler argument.
+message.app-image-requires-identifier=When using an external app image you must specify the identifier.
+message.app-image-requires-identifier.advice=Set the identifier via the -appId CLI flag, the fx:application/@id ANT attribute, or via the 'identifier' bundler argument.
+message.building-dmg=Building DMG package for {0}
+message.running-script=Running shell script on application image [{0}]
+message.intermediate-image-location=[DEBUG] Intermediate application bundle image\: {0}
+message.preparing-dmg-setup=Preparing dmg setup\: {0}
+message.creating-dmg-file=Creating DMG file\: {0}
+message.dmg-cannot-be-overwritten=Dmg file exists ({0} and can not be removed.
+message.output-to-location=Result DMG installer for {0}\: {1}
+message.building-pkg=Building PKG package for {0}
+message.preparing-scripts=Preparing package scripts
+message.preparing-distribution-dist=Preparing distribution.dist\: {0}
+message.signing.pkg=Warning: For signing PKG, you might need to set "Always Trust" for your certificate using "Keychain Access" tool.
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/Runtime-Info.plist.template	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+        <key>CFBundleDevelopmentRegion</key>
+        <string>English</string>
+        <key>CFBundleExecutable</key>
+        <string>libjli.dylib</string>
+        <key>CFBundleIdentifier</key>
+        <string>CF_BUNDLE_IDENTIFIER</string>
+        <key>CFBundleInfoDictionaryVersion</key>
+        <string>7.0</string>
+        <key>CFBundleName</key>
+        <string>CF_BUNDLE_NAME</string>
+        <key>CFBundlePackageType</key>
+        <string>BNDL</string>
+        <key>CFBundleShortVersionString</key>
+        <string>CF_BUNDLE_SHORT_VERSION_STRING</string>
+        <key>CFBundleSignature</key>
+        <string>????</string>
+        <key>CFBundleVersion</key>
+        <string>CF_BUNDLE_VERSION</string>
+</dict>
+</plist>
Binary file src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/background_dmg.png has changed
Binary file src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/background_pkg.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/launchd.plist.template	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+    <key>Label</key>
+    <string>DEPLOY_DAEMON_IDENTIFIER</string>
+    <key>ProgramArguments</key>
+    <array>
+        <string>DEPLOY_DAEMON_LAUNCHER_PATH</string>
+    </array>
+    <key>RunAtLoad</key><DEPLOY_RUN_AT_LOAD/>
+    <key>KeepAlive</key><DEPLOY_KEEP_ALIVE/>
+</dict>
+</plist>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/lic_template.plist	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,244 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>LPic</key>
+	<array>
+		<dict>
+			<key>Attributes</key>
+			<string>0x0000</string>
+			<key>Data</key>
+			<data>AAAAAgAAAAAAAAAAAAQAAA==</data>
+			<key>ID</key>
+			<string>5000</string>
+			<key>Name</key>
+			<string></string>
+		</dict>
+	</array>
+	<key>STR#</key>
+	<array>
+		<dict>
+			<key>Attributes</key>
+			<string>0x0000</string>
+			<key>Data</key>
+			<data>AAYPRW5nbGlzaCBkZWZhdWx0BUFncmVlCERpc2FncmVlBVByaW50B1NhdmUuLi56SWYgeW91IGFncmVlIHdpdGggdGhlIHRlcm1zIG9mIHRoaXMgbGljZW5zZSwgY2xpY2sgIkFncmVlIiB0byBhY2Nlc3MgdGhlIHNvZnR3YXJlLiAgSWYgeW91IGRvIG5vdCBhZ3JlZSwgcHJlc3MgIkRpc2FncmVlLiI=</data>
+			<key>ID</key>
+			<string>5000</string>
+			<key>Name</key>
+			<string>English buttons</string>
+		</dict>
+		<dict>
+			<key>Attributes</key>
+			<string>0x0000</string>
+			<key>Data</key>
+			<data>AAYHRGV1dHNjaAtBa3plcHRpZXJlbghBYmxlaG5lbgdEcnVja2VuClNpY2hlcm4uLi7nS2xpY2tlbiBTaWUgaW4g0kFremVwdGllcmVu0ywgd2VubiBTaWUgbWl0IGRlbiBCZXN0aW1tdW5nZW4gZGVzIFNvZnR3YXJlLUxpemVuenZlcnRyYWdzIGVpbnZlcnN0YW5kZW4gc2luZC4gRmFsbHMgbmljaHQsIGJpdHRlINJBYmxlaG5lbtMgYW5rbGlja2VuLiBTaWUga5pubmVuIGRpZSBTb2Z0d2FyZSBudXIgaW5zdGFsbGllcmVuLCB3ZW5uIFNpZSDSQWt6ZXB0aWVyZW7TIGFuZ2VrbGlja3QgaGFiZW4u</data>
+			<key>ID</key>
+			<string>5001</string>
+			<key>Name</key>
+			<string>German</string>
+		</dict>
+		<dict>
+			<key>Attributes</key>
+			<string>0x0000</string>
+			<key>Data</key>
+			<data>AAYHRW5nbGlzaAVBZ3JlZQhEaXNhZ3JlZQVQcmludAdTYXZlLi4ue0lmIHlvdSBhZ3JlZSB3aXRoIHRoZSB0ZXJtcyBvZiB0aGlzIGxpY2Vuc2UsIHByZXNzICJBZ3JlZSIgdG8gaW5zdGFsbCB0aGUgc29mdHdhcmUuICBJZiB5b3UgZG8gbm90IGFncmVlLCBwcmVzcyAiRGlzYWdyZWUiLg==</data>
+			<key>ID</key>
+			<string>5002</string>
+			<key>Name</key>
+			<string>English</string>
+		</dict>
+		<dict>
+			<key>Attributes</key>
+			<string>0x0000</string>
+			<key>Data</key>
+			<data>AAYHRXNwYZZvbAdBY2VwdGFyCk5vIGFjZXB0YXIISW1wcmltaXIKR3VhcmRhci4uLsBTaSBlc3SHIGRlIGFjdWVyZG8gY29uIGxvcyB0jnJtaW5vcyBkZSBlc3RhIGxpY2VuY2lhLCBwdWxzZSAiQWNlcHRhciIgcGFyYSBpbnN0YWxhciBlbCBzb2Z0d2FyZS4gRW4gZWwgc3VwdWVzdG8gZGUgcXVlIG5vIGVzdI4gZGUgYWN1ZXJkbyBjb24gbG9zIHSOcm1pbm9zIGRlIGVzdGEgbGljZW5jaWEsIHB1bHNlICJObyBhY2VwdGFyLiI=</data>
+			<key>ID</key>
+			<string>5003</string>
+			<key>Name</key>
+			<string>Spanish</string>
+		</dict>
+		<dict>
+			<key>Attributes</key>
+			<string>0x0000</string>
+			<key>Data</key>
+			<data>AAYIRnJhbo1haXMIQWNjZXB0ZXIHUmVmdXNlcghJbXByaW1lcg5FbnJlZ2lzdHJlci4uLrpTaSB2b3VzIGFjY2VwdGV6IGxlcyB0ZXJtZXMgZGUgbGEgcHKOc2VudGUgbGljZW5jZSwgY2xpcXVleiBzdXIgIkFjY2VwdGVyIiBhZmluIGQnaW5zdGFsbGVyIGxlIGxvZ2ljaWVsLiBTaSB2b3VzIG4nkHRlcyBwYXMgZCdhY2NvcmQgYXZlYyBsZXMgdGVybWVzIGRlIGxhIGxpY2VuY2UsIGNsaXF1ZXogc3VyICJSZWZ1c2VyIi4=</data>
+			<key>ID</key>
+			<string>5004</string>
+			<key>Name</key>
+			<string>French</string>
+		</dict>
+		<dict>
+			<key>Attributes</key>
+			<string>0x0000</string>
+			<key>Data</key>
+			<data>AAYISXRhbGlhbm8HQWNjZXR0bwdSaWZpdXRvBlN0YW1wYQtSZWdpc3RyYS4uLn9TZSBhY2NldHRpIGxlIGNvbmRpemlvbmkgZGkgcXVlc3RhIGxpY2VuemEsIGZhaSBjbGljIHN1ICJBY2NldHRvIiBwZXIgaW5zdGFsbGFyZSBpbCBzb2Z0d2FyZS4gQWx0cmltZW50aSBmYWkgY2xpYyBzdSAiUmlmaXV0byIu</data>
+			<key>ID</key>
+			<string>5005</string>
+			<key>Name</key>
+			<string>Italian</string>
+		</dict>
+		<dict>
+			<key>Attributes</key>
+			<string>0x0000</string>
+			<key>Data</key>
+			<data>AAYISmFwYW5lc2UKk6+I04K1gtyCtwyTr4jTgrWC3IK5gvEIiPON/IK3gukHlduRti4uLrSWe4Ncg3SDZ4NFg0eDQY5nl3CLlpH4jF+W8YLMj/CMj4LJk6+I04KzguqC6Y/qjYeCyYLNgUGDXIN0g2eDRYNHg0GC8INDg5ODWINngVuDi4K3gumCvYLfgsmBdZOviNOCtYLcgreBdoLwiZ+CtYLEgq2CvoKzgqKBQoFAk6+I04KzguqCyIKij+qNh4LJgs2BQYF1k6+I04K1gtyCuYLxgXaC8ImfgrWCxIKtgr6Cs4KigUI=</data>
+			<key>ID</key>
+			<string>5006</string>
+			<key>Name</key>
+			<string>Japanese</string>
+		</dict>
+		<dict>
+			<key>Attributes</key>
+			<string>0x0000</string>
+			<key>Data</key>
+			<data>AAYKTmVkZXJsYW5kcwJKYQNOZWUFUHJpbnQJQmV3YWFyLi4upEluZGllbiB1IGFra29vcmQgZ2FhdCBtZXQgZGUgdm9vcndhYXJkZW4gdmFuIGRlemUgbGljZW50aWUsIGt1bnQgdSBvcCAnSmEnIGtsaWtrZW4gb20gZGUgcHJvZ3JhbW1hdHV1ciB0ZSBpbnN0YWxsZXJlbi4gSW5kaWVuIHUgbmlldCBha2tvb3JkIGdhYXQsIGtsaWt0IHUgb3AgJ05lZScu</data>
+			<key>ID</key>
+			<string>5007</string>
+			<key>Name</key>
+			<string>Dutch</string>
+		</dict>
+		<dict>
+			<key>Attributes</key>
+			<string>0x0000</string>
+			<key>Data</key>
+			<data>AAYGU3ZlbnNrCEdvZGuKbm5zBkF2YppqcwhTa3JpdiB1dAhTcGFyYS4uLpNPbSBEdSBnb2Rrim5uZXIgbGljZW5zdmlsbGtvcmVuIGtsaWNrYSBwjCAiR29ka4pubnMiIGaaciBhdHQgaW5zdGFsbGVyYSBwcm9ncmFtcHJvZHVrdGVuLiBPbSBEdSBpbnRlIGdvZGuKbm5lciBsaWNlbnN2aWxsa29yZW4sIGtsaWNrYSBwjCAiQXZimmpzIi4=</data>
+			<key>ID</key>
+			<string>5008</string>
+			<key>Name</key>
+			<string>Swedish</string>
+		</dict>
+		<dict>
+			<key>Attributes</key>
+			<string>0x0000</string>
+			<key>Data</key>
+			<data>AAYRUG9ydHVndZBzLCBCcmFzaWwJQ29uY29yZGFyCURpc2NvcmRhcghJbXByaW1pcglTYWx2YXIuLi6MU2UgZXN0hyBkZSBhY29yZG8gY29tIG9zIHRlcm1vcyBkZXN0YSBsaWNlbo1hLCBwcmVzc2lvbmUgIkNvbmNvcmRhciIgcGFyYSBpbnN0YWxhciBvIHNvZnR3YXJlLiBTZSBui28gZXN0hyBkZSBhY29yZG8sIHByZXNzaW9uZSAiRGlzY29yZGFyIi4=</data>
+			<key>ID</key>
+			<string>5009</string>
+			<key>Name</key>
+			<string>Brazilian Portuguese</string>
+		</dict>
+		<dict>
+			<key>Attributes</key>
+			<string>0x0000</string>
+			<key>Data</key>
+			<data>AAYSU2ltcGxpZmllZCBDaGluZXNlBM2s0uIGsrvNrNLiBLTy06EGtOa0oqGtVMjnufvE+s2s0uKxvtDtv8nQrdLptcTM9b/uo6zH67C0obDNrNLiobHAtLCy17C0y8jtvP6ho8jnufvE+rK7zazS4qOsx+uwtKGwsrvNrNLiobGhow==</data>
+			<key>ID</key>
+			<string>5010</string>
+			<key>Name</key>
+			<string>Simplified Chinese</string>
+		</dict>
+		<dict>
+			<key>Attributes</key>
+			<string>0x0000</string>
+			<key>Data</key>
+			<data>AAYTVHJhZGl0aW9uYWwgQ2hpbmVzZQSmULdOBqSjplC3TgSmQ6ZMBsB4pnOhS1CmcKpHsXqmULdOpbuzXKVpw9K4zKq6sfi02qFBvdCr9qGnplC3TqGopUimd7jLs27F6aFDpnCqR6SjplC3TqFBvdCr9qGnpKOmULdOoaihQw==</data>
+			<key>ID</key>
+			<string>5011</string>
+			<key>Name</key>
+			<string>Traditional Chinese</string>
+		</dict>
+		<dict>
+			<key>Attributes</key>
+			<string>0x0000</string>
+			<key>Data</key>
+			<data>AAYFRGFuc2sERW5pZwVVZW5pZwdVZHNrcml2CkFya2l2ZXIuLi6YSHZpcyBkdSBhY2NlcHRlcmVyIGJldGluZ2Vsc2VybmUgaSBsaWNlbnNhZnRhbGVuLCBza2FsIGR1IGtsaWtrZSBwjCDSRW5pZ9MgZm9yIGF0IGluc3RhbGxlcmUgc29mdHdhcmVuLiBLbGlrIHCMINJVZW5pZ9MgZm9yIGF0IGFubnVsbGVyZSBpbnN0YWxsZXJpbmdlbi4=</data>
+			<key>ID</key>
+			<string>5012</string>
+			<key>Name</key>
+			<string>Danish</string>
+		</dict>
+		<dict>
+			<key>Attributes</key>
+			<string>0x0000</string>
+			<key>Data</key>
+			<data>AAYFU3VvbWkISHl2imtzeW4KRW4gaHl2imtzeQdUdWxvc3RhCVRhbGxlbm5hyW9IeXaKa3N5IGxpc2Vuc3Npc29waW11a3NlbiBlaGRvdCBvc29pdHRhbWFsbGEg1Uh5doprc3nVLiBKb3MgZXQgaHl2imtzeSBzb3BpbXVrc2VuIGVodG9qYSwgb3NvaXRhINVFbiBoeXaKa3N51S4=</data>
+			<key>ID</key>
+			<string>5013</string>
+			<key>Name</key>
+			<string>Finnish</string>
+		</dict>
+		<dict>
+			<key>Attributes</key>
+			<string>0x0000</string>
+			<key>Data</key>
+			<data>AAYRRnJhbo1haXMgY2FuYWRpZW4IQWNjZXB0ZXIHUmVmdXNlcghJbXByaW1lcg5FbnJlZ2lzdHJlci4uLrpTaSB2b3VzIGFjY2VwdGV6IGxlcyB0ZXJtZXMgZGUgbGEgcHKOc2VudGUgbGljZW5jZSwgY2xpcXVleiBzdXIgIkFjY2VwdGVyIiBhZmluIGQnaW5zdGFsbGVyIGxlIGxvZ2ljaWVsLiBTaSB2b3VzIG4nkHRlcyBwYXMgZCdhY2NvcmQgYXZlYyBsZXMgdGVybWVzIGRlIGxhIGxpY2VuY2UsIGNsaXF1ZXogc3VyICJSZWZ1c2VyIi4=</data>
+			<key>ID</key>
+			<string>5014</string>
+			<key>Name</key>
+			<string>French Canadian</string>
+		</dict>
+		<dict>
+			<key>Attributes</key>
+			<string>0x0000</string>
+			<key>Data</key>
+			<data>AAYGS29yZWFuBLW/wMcJtb/AxyC+yMfUBsfBuLDGrgfA+sDlLi4ufrvnv+sgsOi+4LytwMcgs7u/67+hILW/wMfHz7jpLCAitb/AxyIgtNzD37imILStt68gvNLHwcauv/6+7rimILyzxKHHz73KvcO/wC4gtb/Ax8fPwfYgvsq0wrTZuOksICK1v8DHIL7Ix9QiILTcw9+4piC0qbijvcq9w7/ALg==</data>
+			<key>ID</key>
+			<string>5015</string>
+			<key>Name</key>
+			<string>Korean</string>
+		</dict>
+		<dict>
+			<key>Attributes</key>
+			<string>0x0000</string>
+			<key>Data</key>
+			<data>AAYFTm9yc2sERW5pZwlJa2tlIGVuaWcIU2tyaXYgdXQKQXJraXZlci4uLqNIdmlzIERlIGVyIGVuaWcgaSBiZXN0ZW1tZWxzZW5lIGkgZGVubmUgbGlzZW5zYXZ0YWxlbiwga2xpa2tlciBEZSBwjCAiRW5pZyIta25hcHBlbiBmb3IgjCBpbnN0YWxsZXJlIHByb2dyYW12YXJlbi4gSHZpcyBEZSBpa2tlIGVyIGVuaWcsIGtsaWtrZXIgRGUgcIwgIklra2UgZW5pZyIu</data>
+			<key>ID</key>
+			<string>5016</string>
+			<key>Name</key>
+			<string>Norwegian</string>
+		</dict>
+	</array>
+	<key>TEXT</key>
+	<array>
+		<dict>
+			<key>Attributes</key>
+			<string>0x0000</string>
+			<key>Data</key>
+			<data>APPLICATION_LICENSE_TEXT</data>
+			<key>ID</key>
+			<string>5000</string>
+			<key>Name</key>
+			<string>English SLA</string>
+		</dict>
+	</array>
+	<key>TMPL</key>
+	<array>
+		<dict>
+			<key>Attributes</key>
+			<string>0x0000</string>
+			<key>Data</key>
+			<data>E0RlZmF1bHQgTGFuZ3VhZ2UgSUREV1JEBUNvdW50T0NOVAQqKioqTFNUQwtzeXMgbGFuZyBJRERXUkQebG9jYWwgcmVzIElEIChvZmZzZXQgZnJvbSA1MDAwRFdSRBAyLWJ5dGUgbGFuZ3VhZ2U/RFdSRAQqKioqTFNURQ==</data>
+			<key>ID</key>
+			<string>128</string>
+			<key>Name</key>
+			<string>LPic</string>
+		</dict>
+	</array>
+	<key>plst</key>
+	<array>
+		<dict>
+			<key>Attributes</key>
+			<string>0x0050</string>
+			<key>Data</key>
+			<data>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA</data>
+			<key>ID</key>
+			<string>0</string>
+			<key>Name</key>
+			<string></string>
+		</dict>
+	</array>
+	<key>styl</key>
+	<array>
+		<dict>
+			<key>Attributes</key>
+			<string>0x0000</string>
+			<key>Data</key>
+			<data>AAMAAAAAAAwACQAUAAAAAAAAAAAAAAAAACcADAAJABQBAAAAAAAAAAAAAAAAKgAMAAkAFAAAAAAAAAAAAAA=</data>
+			<key>ID</key>
+			<string>5000</string>
+			<key>Name</key>
+			<string>English SLA</string>
+		</dict>
+	</array>
+</dict>
+</plist>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/postinstall.template	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,6 @@
+#!/usr/bin/env sh
+
+set -e
+launchctl load "/Library/LaunchDaemons/DEPLOY_LAUNCHD_PLIST_FILE"
+
+exit 0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/preinstall.template	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,8 @@
+#!/usr/bin/env sh
+
+set -e
+if launchctl list "DEPLOY_DAEMON_IDENTIFIER" &> /dev/null; then
+    launchctl unload "/Library/LaunchDaemons/DEPLOY_LAUNCHD_PLIST_FILE"
+fi
+
+exit 0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/macosx/classes/module-info.java.extra	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+provides jdk.jpackage.internal.Bundler with
+    jdk.jpackage.internal.MacAppBundler,
+    jdk.jpackage.internal.MacAppStoreBundler,
+    jdk.jpackage.internal.MacDmgBundler,
+    jdk.jpackage.internal.MacPkgBundler;
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/macosx/native/jpackageapplauncher/main.m	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2012, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#import <Cocoa/Cocoa.h>
+#include <dlfcn.h>
+#include <unistd.h>
+
+typedef bool (*start_launcher)(int argc, char* argv[]);
+typedef void (*stop_launcher)();
+
+int main(int argc, char *argv[]) {
+#if !__has_feature(objc_arc)
+    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+#endif
+
+    int result = 1;
+
+    @try {
+        setlocale(LC_ALL, "en_US.utf8");
+
+        NSBundle *mainBundle = [NSBundle mainBundle];
+        NSString *mainBundlePath = [mainBundle bundlePath];
+        NSString *libraryName = [mainBundlePath stringByAppendingPathComponent:@"Contents/MacOS/libapplauncher.dylib"];
+
+        void* library = dlopen([libraryName UTF8String], RTLD_LAZY);
+
+        if (library == NULL) {
+            NSLog(@"%@ not found.\n", libraryName);
+        }
+
+        if (library != NULL) {
+            start_launcher start =
+                    (start_launcher)dlsym(library, "start_launcher");
+            stop_launcher stop =
+                    (stop_launcher)dlsym(library, "stop_launcher");
+
+            if (start != NULL && stop != NULL) {
+                if (start(argc, argv) == true) {
+                    result = 0;
+                    stop();
+                }
+            } else if (start == NULL) {
+                NSLog(@"start_launcher not found in %@.\n", libraryName);
+            } else {
+                NSLog(@"stop_launcher not found in %@.\n", libraryName);
+            }
+            dlclose(library);
+        }
+    } @catch (NSException *exception) {
+        NSLog(@"%@: %@", exception, [exception callStackSymbols]);
+        result = 1;
+    }
+
+#if !__has_feature(objc_arc)
+    [pool drain];
+#endif
+
+    return result;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/macosx/native/libapplauncher/MacPlatform.h	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2014, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef MACPLATFORM_H
+#define MACPLATFORM_H
+
+#include "Platform.h"
+#include "PosixPlatform.h"
+
+class MacPlatform : virtual public Platform, PosixPlatform {
+private:
+    bool UsePListForConfigFile();
+
+protected:
+    virtual TString getTmpDirString();
+
+public:
+    MacPlatform(void);
+    virtual ~MacPlatform(void);
+
+public:
+    virtual void ShowMessage(TString title, TString description);
+    virtual void ShowMessage(TString description);
+
+    virtual TCHAR* ConvertStringToFileSystemString(
+            TCHAR* Source, bool &release);
+    virtual TCHAR* ConvertFileSystemStringToString(
+            TCHAR* Source, bool &release);
+
+    virtual void SetCurrentDirectory(TString Value);
+    virtual TString GetPackageRootDirectory();
+    virtual TString GetAppDataDirectory();
+    virtual TString GetBundledJVMLibraryFileName(TString RuntimePath);
+    virtual TString GetAppName();
+
+    TString GetPackageAppDirectory();
+    TString GetPackageLauncherDirectory();
+    TString GetPackageRuntimeBinDirectory();
+
+    virtual ISectionalPropertyContainer* GetConfigFile(TString FileName);
+    virtual TString GetModuleFileName();
+
+    virtual bool IsMainThread();
+    virtual TPlatformNumber GetMemorySize();
+
+    virtual std::map<TString, TString> GetKeys();
+};
+
+
+#endif // MACPLATFORM_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/macosx/native/libapplauncher/MacPlatform.mm	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,513 @@
+/*
+ * Copyright (c) 2014, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "Platform.h"
+
+#include "MacPlatform.h"
+#include "Helpers.h"
+#include "Package.h"
+#include "PropertyFile.h"
+#include "IniFile.h"
+
+#include <sys/sysctl.h>
+#include <pthread.h>
+#include <vector>
+#include <signal.h>
+#include <mach-o/dyld.h>
+
+#import <Foundation/Foundation.h>
+#import <AppKit/NSRunningApplication.h>
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <CoreFoundation/CFString.h>
+
+#ifdef __OBJC__
+#import <Cocoa/Cocoa.h>
+#endif //__OBJC__
+
+#define MAC_JPACKAGE_TMP_DIR \
+        "/Library/Application Support/Java/JPackage/tmp"
+
+NSString* StringToNSString(TString Value) {
+    NSString* result = [NSString stringWithCString : Value.c_str()
+            encoding : [NSString defaultCStringEncoding]];
+    return result;
+}
+
+FileSystemStringToString::FileSystemStringToString(const TCHAR* value) {
+    bool release = false;
+    PlatformString lvalue = PlatformString(value);
+    Platform& platform = Platform::GetInstance();
+    TCHAR* buffer = platform.ConvertFileSystemStringToString(lvalue, release);
+    FData = buffer;
+
+    if (buffer != NULL && release == true) {
+        delete[] buffer;
+    }
+}
+
+FileSystemStringToString::operator TString() {
+    return FData;
+}
+
+StringToFileSystemString::StringToFileSystemString(const TString &value) {
+    FRelease = false;
+    PlatformString lvalue = PlatformString(value);
+    Platform& platform = Platform::GetInstance();
+    FData = platform.ConvertStringToFileSystemString(lvalue, FRelease);
+}
+
+StringToFileSystemString::~StringToFileSystemString() {
+    if (FRelease == true) {
+        delete[] FData;
+    }
+}
+
+StringToFileSystemString::operator TCHAR* () {
+    return FData;
+}
+
+MacPlatform::MacPlatform(void) : Platform(), PosixPlatform() {
+}
+
+MacPlatform::~MacPlatform(void) {
+}
+
+TString MacPlatform::GetPackageAppDirectory() {
+    return FilePath::IncludeTrailingSeparator(
+            GetPackageRootDirectory()) + _T("Java");
+}
+
+TString MacPlatform::GetPackageLauncherDirectory() {
+    return FilePath::IncludeTrailingSeparator(
+            GetPackageRootDirectory()) + _T("MacOS");
+}
+
+TString MacPlatform::GetPackageRuntimeBinDirectory() {
+    return FilePath::IncludeTrailingSeparator(GetPackageRootDirectory()) +
+            _T("Plugins/Java.runtime/Contents/Home/bin");
+}
+
+bool MacPlatform::UsePListForConfigFile() {
+    return FilePath::FileExists(GetConfigFileName()) == false;
+}
+
+void MacPlatform::ShowMessage(TString Title, TString Description) {
+    NSString *ltitle = StringToNSString(Title);
+    NSString *ldescription = StringToNSString(Description);
+
+    NSLog(@"%@:%@", ltitle, ldescription);
+}
+
+void MacPlatform::ShowMessage(TString Description) {
+    TString appname = GetModuleFileName();
+    appname = FilePath::ExtractFileName(appname);
+    ShowMessage(appname, Description);
+}
+
+TString MacPlatform::getTmpDirString() {
+    return TString(MAC_JPACKAGE_TMP_DIR);
+}
+
+TCHAR* MacPlatform::ConvertStringToFileSystemString(TCHAR* Source,
+        bool &release) {
+    TCHAR* result = NULL;
+    release = false;
+    CFStringRef StringRef = CFStringCreateWithCString(kCFAllocatorDefault,
+            Source, kCFStringEncodingUTF8);
+
+    if (StringRef != NULL) {
+        @ try {
+            CFIndex length =
+                    CFStringGetMaximumSizeOfFileSystemRepresentation(StringRef);
+            result = new char[length + 1];
+            if (result != NULL) {
+                if (CFStringGetFileSystemRepresentation(StringRef,
+                        result, length)) {
+                    release = true;
+                } else {
+                    delete[] result;
+                    result = NULL;
+                }
+            }
+        }
+        @finally
+        {
+            CFRelease(StringRef);
+        }
+    }
+
+    return result;
+}
+
+TCHAR* MacPlatform::ConvertFileSystemStringToString(TCHAR* Source,
+        bool &release) {
+    TCHAR* result = NULL;
+    release = false;
+    CFStringRef StringRef = CFStringCreateWithFileSystemRepresentation(
+            kCFAllocatorDefault, Source);
+
+    if (StringRef != NULL) {
+        @ try {
+            CFIndex length = CFStringGetLength(StringRef);
+
+            if (length > 0) {
+                CFIndex maxSize = CFStringGetMaximumSizeForEncoding(
+                        length, kCFStringEncodingUTF8);
+
+                result = new char[maxSize + 1];
+                if (result != NULL) {
+                    if (CFStringGetCString(StringRef, result, maxSize,
+                            kCFStringEncodingUTF8) == true) {
+                        release = true;
+                    } else {
+                        delete[] result;
+                        result = NULL;
+                    }
+                }
+            }
+        }
+        @finally
+        {
+            CFRelease(StringRef);
+        }
+    }
+
+    return result;
+}
+
+void MacPlatform::SetCurrentDirectory(TString Value) {
+    chdir(PlatformString(Value).toPlatformString());
+}
+
+TString MacPlatform::GetPackageRootDirectory() {
+    NSBundle *mainBundle = [NSBundle mainBundle];
+    NSString *mainBundlePath = [mainBundle bundlePath];
+    NSString *contentsPath =
+            [mainBundlePath stringByAppendingString : @"/Contents"];
+    TString result = [contentsPath UTF8String];
+    return result;
+}
+
+TString MacPlatform::GetAppDataDirectory() {
+    TString result;
+    NSArray *paths = NSSearchPathForDirectoriesInDomains(
+            NSApplicationSupportDirectory, NSUserDomainMask, YES);
+    NSString *applicationSupportDirectory = [paths firstObject];
+    result = [applicationSupportDirectory UTF8String];
+    return result;
+}
+
+TString MacPlatform::GetBundledJVMLibraryFileName(TString RuntimePath) {
+    TString result;
+
+    // first try lib/, then lib/jli
+    result = FilePath::IncludeTrailingSeparator(RuntimePath) +
+            _T("Contents/Home/lib/libjli.dylib");
+
+    if (FilePath::FileExists(result) == false) {
+        result = FilePath::IncludeTrailingSeparator(RuntimePath) +
+                _T("Contents/Home/lib/jli/libjli.dylib");
+
+        if (FilePath::FileExists(result) == false) {
+            // cannot find
+            NSLog(@"Cannot find libjli.dysym!");
+            result = _T("");
+        }
+    }
+
+    return result;
+}
+
+TString MacPlatform::GetAppName() {
+    NSString *appName = [[NSProcessInfo processInfo] processName];
+    TString result = [appName UTF8String];
+    return result;
+}
+
+void PosixProcess::Cleanup() {
+    if (FOutputHandle != 0) {
+        close(FOutputHandle);
+        FOutputHandle = 0;
+    }
+
+    if (FInputHandle != 0) {
+        close(FInputHandle);
+        FInputHandle = 0;
+    }
+
+    sigaction(SIGINT, &savintr, (struct sigaction *) 0);
+    sigaction(SIGQUIT, &savequit, (struct sigaction *) 0);
+    sigprocmask(SIG_SETMASK, &saveblock, (sigset_t *) 0);
+}
+
+#define PIPE_READ 0
+#define PIPE_WRITE 1
+
+bool PosixProcess::Execute(const TString Application,
+        const std::vector<TString> Arguments, bool AWait) {
+    bool result = false;
+
+    if (FRunning == false) {
+        FRunning = true;
+
+        int handles[2];
+
+        if (pipe(handles) == -1) {
+            return false;
+        }
+
+        struct sigaction sa;
+        sa.sa_handler = SIG_IGN;
+        sigemptyset(&sa.sa_mask);
+        sa.sa_flags = 0;
+        sigemptyset(&savintr.sa_mask);
+        sigemptyset(&savequit.sa_mask);
+        sigaction(SIGINT, &sa, &savintr);
+        sigaction(SIGQUIT, &sa, &savequit);
+        sigaddset(&sa.sa_mask, SIGCHLD);
+        sigprocmask(SIG_BLOCK, &sa.sa_mask, &saveblock);
+
+        FChildPID = fork();
+
+        // PID returned by vfork is 0 for the child process and the
+        // PID of the child process for the parent.
+        if (FChildPID == -1) {
+            // Error
+            TString message = PlatformString::Format(
+                    _T("Error: Unable to create process %s"),
+                    Application.data());
+            throw Exception(message);
+        } else if (FChildPID == 0) {
+            Cleanup();
+            TString command = Application;
+
+            for (std::vector<TString>::const_iterator iterator =
+                    Arguments.begin(); iterator != Arguments.end();
+                    iterator++) {
+                command += TString(_T(" ")) + *iterator;
+            }
+#ifdef DEBUG
+            printf("%s\n", command.data());
+#endif // DEBUG
+
+            dup2(handles[PIPE_READ], STDIN_FILENO);
+            dup2(handles[PIPE_WRITE], STDOUT_FILENO);
+
+            close(handles[PIPE_READ]);
+            close(handles[PIPE_WRITE]);
+
+            execl("/bin/sh", "sh", "-c", command.data(), (char *) 0);
+
+            _exit(127);
+        } else {
+            FOutputHandle = handles[PIPE_READ];
+            FInputHandle = handles[PIPE_WRITE];
+
+            if (AWait == true) {
+                ReadOutput();
+                Wait();
+                Cleanup();
+                FRunning = false;
+                result = true;
+            } else {
+                result = true;
+            }
+        }
+    }
+
+    return result;
+}
+
+void AppendPListArrayToIniFile(NSDictionary *infoDictionary,
+        IniFile *result, TString Section) {
+    NSString *sectionKey =
+            [NSString stringWithUTF8String : PlatformString(Section).toMultibyte()];
+    NSDictionary *array = [infoDictionary objectForKey : sectionKey];
+
+    for (id option in array) {
+        if ([option isKindOfClass : [NSString class]]) {
+            TString arg = [option UTF8String];
+
+            TString name;
+            TString value;
+
+            if (Helpers::SplitOptionIntoNameValue(arg, name, value) == true) {
+                result->Append(Section, name, value);
+            }
+        }
+    }
+}
+
+void AppendPListDictionaryToIniFile(NSDictionary *infoDictionary,
+        IniFile *result, TString Section, bool FollowSection = true) {
+    NSDictionary *dictionary = NULL;
+
+    if (FollowSection == true) {
+        NSString *sectionKey = [NSString stringWithUTF8String : PlatformString(
+                Section).toMultibyte()];
+        dictionary = [infoDictionary objectForKey : sectionKey];
+    } else {
+        dictionary = infoDictionary;
+    }
+
+    for (id key in dictionary) {
+        id option = [dictionary valueForKey : key];
+
+        if ([key isKindOfClass : [NSString class]] &&
+                [option isKindOfClass : [NSString class]]) {
+            TString name = [key UTF8String];
+            TString value = [option UTF8String];
+            result->Append(Section, name, value);
+        }
+    }
+}
+
+// Convert parts of the info.plist to the INI format the rest of the jpackage
+// uses unless a jpackage config file exists.
+ISectionalPropertyContainer* MacPlatform::GetConfigFile(TString FileName) {
+    IniFile* result = new IniFile();
+    if (result == NULL) {
+        return NULL;
+    }
+
+    if (UsePListForConfigFile() == false) {
+        if (result->LoadFromFile(FileName) == false) {
+            // New property file format was not found,
+            // attempt to load old property file format.
+            Helpers::LoadOldConfigFile(FileName, result);
+        }
+    } else {
+        NSBundle *mainBundle = [NSBundle mainBundle];
+        NSDictionary *infoDictionary = [mainBundle infoDictionary];
+        std::map<TString, TString> keys = GetKeys();
+
+        // JPackage options.
+        AppendPListDictionaryToIniFile(infoDictionary, result,
+                keys[CONFIG_SECTION_APPLICATION], false);
+
+        // jvmargs
+        AppendPListArrayToIniFile(infoDictionary, result,
+                keys[CONFIG_SECTION_JVMOPTIONS]);
+
+        // Generate AppCDS Cache
+        AppendPListDictionaryToIniFile(infoDictionary, result,
+                keys[CONFIG_SECTION_APPCDSJVMOPTIONS]);
+        AppendPListDictionaryToIniFile(infoDictionary, result,
+                keys[CONFIG_SECTION_APPCDSGENERATECACHEJVMOPTIONS]);
+
+        // args
+        AppendPListArrayToIniFile(infoDictionary, result,
+                keys[CONFIG_SECTION_ARGOPTIONS]);
+    }
+
+    return result;
+}
+
+TString GetModuleFileNameOSX() {
+    Dl_info module_info;
+    if (dladdr(reinterpret_cast<void*> (GetModuleFileNameOSX),
+            &module_info) == 0) {
+        // Failed to find the symbol we asked for.
+        return std::string();
+    }
+    return TString(module_info.dli_fname);
+}
+
+TString MacPlatform::GetModuleFileName() {
+    TString result;
+    DynamicBuffer<TCHAR> buffer(MAX_PATH);
+    uint32_t size = buffer.GetSize();
+
+    if (_NSGetExecutablePath(buffer.GetData(), &size) == 0) {
+        result = FileSystemStringToString(buffer.GetData());
+    }
+
+    return result;
+}
+
+bool MacPlatform::IsMainThread() {
+    bool result = (pthread_main_np() == 1);
+    return result;
+}
+
+TPlatformNumber MacPlatform::GetMemorySize() {
+    unsigned long long memory = [[NSProcessInfo processInfo] physicalMemory];
+
+    // Convert from bytes to megabytes.
+    TPlatformNumber result = memory / 1048576;
+
+    return result;
+}
+
+std::map<TString, TString> MacPlatform::GetKeys() {
+    std::map<TString, TString> keys;
+
+    if (UsePListForConfigFile() == false) {
+        return Platform::GetKeys();
+    } else {
+        keys.insert(std::map<TString, TString>::value_type(CONFIG_VERSION,
+                _T("app.version")));
+        keys.insert(std::map<TString, TString>::value_type(CONFIG_MAINJAR_KEY,
+                _T("JVMMainJarName")));
+        keys.insert(std::map<TString, TString>::value_type(CONFIG_MAINMODULE_KEY,
+                _T("JVMMainModuleName")));
+        keys.insert(std::map<TString, TString>::value_type(
+                CONFIG_MAINCLASSNAME_KEY, _T("JVMMainClassName")));
+        keys.insert(std::map<TString, TString>::value_type(
+                CONFIG_CLASSPATH_KEY, _T("JVMAppClasspath")));
+        keys.insert(std::map<TString, TString>::value_type(APP_NAME_KEY,
+                _T("CFBundleName")));
+        keys.insert(std::map<TString, TString>::value_type(CONFIG_APP_ID_KEY,
+                _T("JVMPreferencesID")));
+        keys.insert(std::map<TString, TString>::value_type(JVM_RUNTIME_KEY,
+                _T("JVMRuntime")));
+        keys.insert(std::map<TString, TString>::value_type(JPACKAGE_APP_DATA_DIR,
+                _T("CFBundleIdentifier")));
+
+        keys.insert(std::map<TString, TString>::value_type(CONFIG_SPLASH_KEY,
+                _T("app.splash")));
+        keys.insert(std::map<TString, TString>::value_type(CONFIG_APP_MEMORY,
+                _T("app.memory")));
+        keys.insert(std::map<TString, TString>::value_type(CONFIG_APP_DEBUG,
+                _T("app.debug")));
+        keys.insert(std::map<TString, TString>::value_type(
+                CONFIG_APPLICATION_INSTANCE, _T("app.application.instance")));
+
+        keys.insert(std::map<TString, TString>::value_type(
+                CONFIG_SECTION_APPLICATION, _T("Application")));
+        keys.insert(std::map<TString, TString>::value_type(
+                CONFIG_SECTION_JVMOPTIONS, _T("JVMOptions")));
+        keys.insert(std::map<TString, TString>::value_type(
+                CONFIG_SECTION_APPCDSJVMOPTIONS, _T("AppCDSJVMOptions")));
+        keys.insert(std::map<TString, TString>::value_type(
+                CONFIG_SECTION_APPCDSGENERATECACHEJVMOPTIONS,
+                _T("AppCDSGenerateCacheJVMOptions")));
+        keys.insert(std::map<TString, TString>::value_type(
+                CONFIG_SECTION_ARGOPTIONS, _T("ArgOptions")));
+    }
+
+    return keys;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/macosx/native/libapplauncher/PlatformDefs.h	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef PLATFORM_DEFS_H
+#define PLATFORM_DEFS_H
+
+#include <errno.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <dlfcn.h>
+#include <libgen.h>
+#include <string>
+
+using namespace std;
+
+#ifndef MAC
+#define MAC
+#endif
+
+#define _T(x) x
+
+typedef char TCHAR;
+typedef std::string TString;
+#define StringLength strlen
+
+typedef unsigned long DWORD;
+
+#define TRAILING_PATHSEPARATOR '/'
+#define BAD_TRAILING_PATHSEPARATOR '\\'
+#define PATH_SEPARATOR ':'
+#define BAD_PATH_SEPARATOR ';'
+#define MAX_PATH 1000
+
+typedef long TPlatformNumber;
+typedef pid_t TProcessID;
+
+#define HMODULE void*
+
+typedef void* Module;
+typedef void* Procedure;
+
+
+// StringToFileSystemString is a stack object. It's usage is
+// simply inline to convert a
+// TString to a file system string. Example:
+//
+// return dlopen(StringToFileSystemString(FileName), RTLD_LAZY);
+//
+class StringToFileSystemString {
+    // Prohibit Heap-Based StringToFileSystemString
+private:
+    static void *operator new(size_t size);
+    static void operator delete(void *ptr);
+
+private:
+    TCHAR* FData;
+    bool FRelease;
+
+public:
+    StringToFileSystemString(const TString &value);
+    ~StringToFileSystemString();
+
+    operator TCHAR* ();
+};
+
+
+// FileSystemStringToString is a stack object. It's usage is
+// simply inline to convert a
+// file system string to a TString. Example:
+//
+// DynamicBuffer<TCHAR> buffer(MAX_PATH);
+// if (readlink("/proc/self/exe", buffer.GetData(), MAX_PATH) != -1)
+//    result = FileSystemStringToString(buffer.GetData());
+//
+class FileSystemStringToString {
+    // Prohibit Heap-Based FileSystemStringToString
+private:
+    static void *operator new(size_t size);
+    static void operator delete(void *ptr);
+
+private:
+    TString FData;
+
+public:
+    FileSystemStringToString(const TCHAR* value);
+
+    operator TString ();
+};
+
+#endif // PLATFORM_DEFS_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/AbstractAppImageBuilder.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,264 @@
+/*
+ * Copyright (c) 2015, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jpackage.internal;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.text.MessageFormat;
+import java.util.List;
+import java.util.Map;
+import java.util.ResourceBundle;
+import java.util.ArrayList;
+
+import jdk.jpackage.internal.resources.ResourceLocator;
+
+import static jdk.jpackage.internal.StandardBundlerParam.*;
+import static jdk.jpackage.internal.StandardBundlerParam.ARGUMENTS;
+
+public abstract class AbstractAppImageBuilder {
+
+    private static final ResourceBundle I18N = ResourceBundle.getBundle(
+            "jdk.jpackage.internal.resources.MainResources");
+
+    private final Map<String, Object> properties;
+    private final Path root;
+    protected List<String> excludeFileList = new ArrayList<>();
+
+    public AbstractAppImageBuilder(Map<String, Object> properties,
+            Path root) throws IOException {
+        this.properties = properties;
+        this.root = root;
+        excludeFileList.add(".*\\.diz");
+    }
+
+    public InputStream getResourceAsStream(String name) {
+        return ResourceLocator.class.getResourceAsStream(name);
+    }
+
+    public abstract void prepareApplicationFiles() throws IOException;
+    public abstract void prepareJreFiles() throws IOException;
+    public abstract Path getAppDir();
+    public abstract Path getAppModsDir();
+
+    public Map<String, Object> getProperties() {
+        return this.properties;
+    }
+
+    public Path getRoot() {
+        return this.root;
+    }
+
+    public String getExcludeFileList() {
+        return String.join(",", excludeFileList);
+    }
+
+    protected void copyEntry(Path appDir, File srcdir, String fname)
+            throws IOException {
+        Path dest = appDir.resolve(fname);
+        Files.createDirectories(dest.getParent());
+        File src = new File(srcdir, fname);
+        if (src.isDirectory()) {
+            IOUtils.copyRecursive(src.toPath(), dest);
+        } else {
+            Files.copy(src.toPath(), dest);
+        }
+    }
+
+    protected InputStream locateResource(String publicName, String category,
+            String defaultName, File customFile,
+            boolean verbose, File publicRoot) throws IOException {
+        InputStream is = null;
+        boolean customFromClasspath = false;
+        boolean customFromFile = false;
+        if (publicName != null) {
+            if (publicRoot != null) {
+                File publicResource = new File(publicRoot, publicName);
+                if (publicResource.exists() && publicResource.isFile()) {
+                    is = new FileInputStream(publicResource);
+                }
+            } else {
+                is = getResourceAsStream(publicName);
+            }
+            customFromClasspath = (is != null);
+        }
+        if (is == null && customFile != null) {
+            is = new FileInputStream(customFile);
+            customFromFile = (is != null);
+        }
+        if (is == null && defaultName != null) {
+            is = getResourceAsStream(defaultName);
+        }
+        if (verbose) {
+            String msg = null;
+            if (customFromClasspath) {
+                msg = MessageFormat.format(I18N.getString(
+                    "message.using-custom-resource"),
+                    category == null ? "" : "[" + category + "] ", publicName);
+            } else if (customFromFile) {
+                msg = MessageFormat.format(I18N.getString(
+                    "message.using-custom-resource-from-file"),
+                    category == null ? "" : "[" + category + "] ",
+                    customFile.getAbsoluteFile());
+            } else if (is != null) {
+                msg = MessageFormat.format(I18N.getString(
+                    "message.using-default-resource"),
+                    defaultName,
+                    category == null ? "" : "[" + category + "] ",
+                    publicName);
+            } else {
+                msg = MessageFormat.format(I18N.getString(
+                    "message.no-default-resource"),
+                    defaultName == null ? "" : defaultName,
+                    category == null ? "" : "[" + category + "] ",
+                    publicName);
+            }
+            if (msg != null) {
+                Log.verbose(msg);
+            }
+        }
+        return is;
+    }
+
+
+    protected String preprocessTextResource(String publicName, String category,
+            String defaultName, Map<String, String> pairs,
+            boolean verbose, File publicRoot) throws IOException {
+        InputStream inp = locateResource(publicName, category,
+                defaultName, null, verbose, publicRoot);
+        if (inp == null) {
+            throw new RuntimeException(
+                    "Module corrupt? No "+defaultName+" resource!");
+        }
+
+        try (InputStream is = inp) {
+            //read fully into memory
+            ByteArrayOutputStream baos = new ByteArrayOutputStream();
+            byte[] buffer = new byte[1024];
+            int length;
+            while ((length = is.read(buffer)) != -1) {
+                baos.write(buffer, 0, length);
+            }
+
+            //substitute
+            String result = new String(baos.toByteArray());
+            for (Map.Entry<String, String> e : pairs.entrySet()) {
+                if (e.getValue() != null) {
+                    result = result.replace(e.getKey(), e.getValue());
+                }
+            }
+            return result;
+        }
+    }
+
+    public void writeCfgFile(Map<String, ? super Object> params,
+            File cfgFileName, String runtimeLocation) throws IOException {
+        cfgFileName.delete();
+
+        File mainJar = JLinkBundlerHelper.getMainJar(params);
+        ModFile.ModType mainJarType = ModFile.ModType.Unknown;
+
+        if (mainJar != null) {
+            mainJarType = new ModFile(mainJar).getModType();
+        }
+
+        String mainModule = StandardBundlerParam.MODULE.fetchFrom(params);
+
+        PrintStream out = new PrintStream(cfgFileName);
+
+        out.println("[Application]");
+        out.println("app.name=" + APP_NAME.fetchFrom(params));
+        out.println("app.version=" + VERSION.fetchFrom(params));
+        out.println("app.preferences.id=" + PREFERENCES_ID.fetchFrom(params));
+        out.println("app.runtime=" + runtimeLocation);
+        out.println("app.identifier=" + IDENTIFIER.fetchFrom(params));
+        out.println("app.classpath=" + String.join(File.pathSeparator,
+                CLASSPATH.fetchFrom(params).split("[ :;]")));
+
+        // The main app is required to be a jar, modular or unnamed.
+        if (mainModule != null &&
+                (mainJarType == ModFile.ModType.Unknown ||
+                mainJarType == ModFile.ModType.ModularJar)) {
+            out.println("app.mainmodule=" + mainModule);
+        } else {
+            String mainClass = JLinkBundlerHelper.getMainClass(params);
+            // If the app is contained in an unnamed jar then launch it the
+            // legacy way and the main class string must be
+            // of the format com/foo/Main
+            if (mainJar != null) {
+                out.println("app.mainjar="
+                        + mainJar.toPath().getFileName().toString());
+            }
+            if (mainClass != null) {
+                out.println("app.mainclass="
+                        + mainClass.replaceAll("\\.", "/"));
+            }
+        }
+
+        Integer port = JLinkBundlerHelper.DEBUG.fetchFrom(params);
+
+        if (port != null) {
+            out.println(
+                    "app.debug=-agentlib:jdwp=transport=dt_socket,"
+                    + "server=y,suspend=y,address=localhost:"
+                    + port);
+        }
+
+        out.println();
+        out.println("[JVMOptions]");
+        List<String> jvmargs = JVM_OPTIONS.fetchFrom(params);
+        for (String arg : jvmargs) {
+            out.println(arg);
+        }
+        Path modsDir = getAppModsDir();
+        if (modsDir != null && modsDir.toFile().exists()) {
+            out.println("--module-path");
+            out.println(getAppDir().relativize(modsDir));
+        }
+
+        out.println();
+        out.println("[ArgOptions]");
+        List<String> args = ARGUMENTS.fetchFrom(params);
+        for (String arg : args) {
+            if (arg.endsWith("=") &&
+                    (arg.indexOf("=") == arg.lastIndexOf("="))) {
+                out.print(arg.substring(0, arg.length() - 1));
+                out.println("\\=");
+            } else {
+                out.println(arg);
+            }
+        }
+
+
+        out.close();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/AbstractBundler.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,196 @@
+/*
+ * Copyright (c) 2014, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jpackage.internal;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.StandardCopyOption;
+import java.nio.file.Files;
+import java.text.MessageFormat;
+import java.util.Map;
+import java.util.ResourceBundle;
+
+import jdk.jpackage.internal.resources.ResourceLocator;
+
+/**
+ * AbstractBundler
+ *
+ * This is the base class all Bundlers extend from.
+ * It contains methods and parameters common to all Bundlers.
+ * The concrete implementations are in the platform specific Bundlers.
+ */
+public abstract class AbstractBundler implements Bundler {
+
+    private static final ResourceBundle I18N = ResourceBundle.getBundle(
+            "jdk.jpackage.internal.resources.MainResources");
+
+    public static final BundlerParamInfo<File> IMAGES_ROOT =
+            new StandardBundlerParam<>(
+            I18N.getString("param.images-root.name"),
+            I18N.getString("param.images-root.description"),
+            "imagesRoot",
+            File.class,
+            params -> new File(
+                StandardBundlerParam.BUILD_ROOT.fetchFrom(params), "images"),
+            (s, p) -> null);
+
+    public InputStream getResourceAsStream(String name) {
+        return ResourceLocator.class.getResourceAsStream(name);
+    }
+
+    protected void fetchResource(String publicName, String category,
+            String defaultName, File result, boolean verbose, File publicRoot)
+            throws IOException {
+
+        InputStream is = streamResource(publicName, category,
+                defaultName, verbose, publicRoot);
+        if (is != null) {
+            try {
+                Files.copy(is, result.toPath(),
+                        StandardCopyOption.REPLACE_EXISTING);
+            } finally {
+                is.close();
+            }
+        } else {
+            if (verbose) {
+                Log.verbose(MessageFormat.format(I18N.getString(
+                        "message.no-default-resource"),
+                        defaultName == null ? "" : defaultName,
+                        category == null ? "" : "[" + category + "] ",
+                        publicName));
+            }
+        }
+    }
+
+    protected void fetchResource(String publicName, String category,
+            File defaultFile, File result, boolean verbose, File publicRoot)
+            throws IOException {
+
+        InputStream is = streamResource(publicName, category,
+                null, verbose, publicRoot);
+        if (is != null) {
+            try {
+                Files.copy(is, result.toPath());
+            } finally {
+                is.close();
+            }
+        } else {
+            IOUtils.copyFile(defaultFile, result);
+            if (verbose) {
+                Log.verbose(MessageFormat.format(I18N.getString(
+                        "message.using-custom-resource-from-file"),
+                        category == null ? "" : "[" + category + "] ",
+                        defaultFile.getAbsoluteFile()));
+            }
+        }
+    }
+
+    private InputStream streamResource(String publicName, String category,
+            String defaultName, boolean verbose, File publicRoot)
+            throws IOException {
+        boolean custom = false;
+        InputStream is = null;
+        if (publicName != null) {
+            if (publicRoot != null) {
+                File publicResource = new File(publicRoot, publicName);
+                if (publicResource.exists() && publicResource.isFile()) {
+                    is = new BufferedInputStream(
+                            new FileInputStream(publicResource));
+                }
+            } else {
+                is = getResourceAsStream(publicName);
+            }
+            custom = (is != null);
+        }
+        if (is == null && defaultName != null) {
+            is = getResourceAsStream(defaultName);
+        }
+        if (verbose && is != null) {
+            String msg = null;
+            if (custom) {
+                msg = MessageFormat.format(I18N.getString(
+                        "message.using-custom-resource"),
+                        category == null ?
+                        "" : "[" + category + "] ", publicName);
+            } else {
+                msg = MessageFormat.format(I18N.getString(
+                        "message.using-default-resource"),
+                        defaultName == null ? "" : defaultName,
+                        category == null ? "" : "[" + category + "] ",
+                        publicName);
+            }
+            Log.verbose(msg);
+        }
+        return is;
+    }
+
+    protected String preprocessTextResource(String publicName, String category,
+            String defaultName, Map<String, String> pairs,
+            boolean verbose, File publicRoot) throws IOException {
+        InputStream inp = streamResource(
+                publicName, category, defaultName, verbose, publicRoot);
+        if (inp == null) {
+            throw new RuntimeException(
+                    "Jar corrupt? No " + defaultName + " resource!");
+        }
+
+        // read fully into memory
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        byte[] buffer = new byte[1024];
+        int length;
+        while ((length = inp.read(buffer)) != -1) {
+            baos.write(buffer, 0, length);
+        }
+
+        // substitute
+        String result = new String(baos.toByteArray());
+        for (Map.Entry<String, String> e : pairs.entrySet()) {
+            if (e.getValue() != null) {
+                result = result.replace(e.getKey(), e.getValue());
+            }
+        }
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return getName();
+    }
+
+    @Override
+    public void cleanup(Map<String, ? super Object> params) {
+        try {
+            IOUtils.deleteRecursive(
+                    StandardBundlerParam.BUILD_ROOT.fetchFrom(params));
+        } catch (IOException e) {
+            Log.debug(e.getMessage());
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/AbstractImageBundler.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2015, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jpackage.internal;
+
+import java.text.MessageFormat;
+import java.util.Map;
+import java.util.ResourceBundle;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.io.File;
+import java.io.IOException;
+
+import static jdk.jpackage.internal.StandardBundlerParam.*;
+
+/**
+ * AbstractImageBundler
+ *
+ * This is the base class for each of the Application Image Bundlers.
+ *
+ * It contains methods and parameters common to all Image Bundlers.
+ *
+ * Application Image Bundlers are created in "create-image" mode,
+ * or as an intermeadiate step in "create-installer" mode.
+ *
+ * The concrete implementations are in the platform specific Bundlers.
+ */
+public abstract class AbstractImageBundler extends AbstractBundler {
+
+    private final static String JAVA_VERSION_SPEC =
+        "java version \"((\\d+).(\\d+).(\\d+).(\\d+))(-(.*))?(\\+[^\"]*)?\"";
+
+    private static final ResourceBundle I18N = ResourceBundle.getBundle(
+            "jdk.jpackage.internal.resources.MainResources");
+
+    public void imageBundleValidation(Map<String, ? super Object> p)
+             throws ConfigException {
+        StandardBundlerParam.validateMainClassInfoFromAppResources(p);
+
+        boolean hasMainJar = MAIN_JAR.fetchFrom(p) != null;
+        boolean hasMainModule =
+                StandardBundlerParam.MODULE.fetchFrom(p) != null;
+        boolean hasMainClass = MAIN_CLASS.fetchFrom(p) != null;
+        boolean runtime = RUNTIME_INSTALLER.fetchFrom(p);
+
+        if (!hasMainJar && !hasMainModule && !hasMainClass && !runtime) {
+            throw new ConfigException(
+                    I18N.getString("error.no-application-class"),
+                    I18N.getString("error.no-application-class.advice"));
+        }
+    }
+
+    public static void extractFlagsFromVersion(
+            Map<String, ? super Object> params, String versionOutput) {
+        Pattern bitArchPattern = Pattern.compile("(\\d*)[- ]?[bB]it");
+        Matcher matcher = bitArchPattern.matcher(versionOutput);
+        if (matcher.find()) {
+            params.put(".runtime.bit-arch", matcher.group(1));
+        } else {
+            // presume 32 bit on no match
+            params.put(".runtime.bit-arch", "32");
+        }
+
+        Pattern oldVersionMatcher = Pattern.compile(
+                "java version \"((\\d+.(\\d+).\\d+)(_(\\d+)))?(-(.*))?\"");
+        matcher = oldVersionMatcher.matcher(versionOutput);
+        if (matcher.find()) {
+            params.put(".runtime.version", matcher.group(1));
+            params.put(".runtime.version.release", matcher.group(2));
+            params.put(".runtime.version.major", matcher.group(3));
+            params.put(".runtime.version.update", matcher.group(5));
+            params.put(".runtime.version.minor", matcher.group(5));
+            params.put(".runtime.version.security", matcher.group(5));
+            params.put(".runtime.version.patch", "0");
+            params.put(".runtime.version.modifiers", matcher.group(7));
+        } else {
+            Pattern newVersionMatcher = Pattern.compile(JAVA_VERSION_SPEC);
+            matcher = newVersionMatcher.matcher(versionOutput);
+            if (matcher.find()) {
+                params.put(".runtime.version", matcher.group(1));
+                params.put(".runtime.version.release", matcher.group(1));
+                params.put(".runtime.version.major", matcher.group(2));
+                params.put(".runtime.version.update", matcher.group(3));
+                params.put(".runtime.version.minor", matcher.group(3));
+                params.put(".runtime.version.security", matcher.group(4));
+                params.put(".runtime.version.patch", matcher.group(5));
+                params.put(".runtime.version.modifiers", matcher.group(7));
+            } else {
+                params.put(".runtime.version", "");
+                params.put(".runtime.version.release", "");
+                params.put(".runtime.version.major", "");
+                params.put(".runtime.version.update", "");
+                params.put(".runtime.version.minor", "");
+                params.put(".runtime.version.security", "");
+                params.put(".runtime.version.patch", "");
+                params.put(".runtime.version.modifiers", "");
+            }
+        }
+    }
+
+    protected File createRoot(Map<String, ? super Object> p,
+            File outputDirectory, boolean dependentTask,
+            String name, String jlinkKey) throws PackagerException {
+        if (!outputDirectory.isDirectory() && !outputDirectory.mkdirs()) {
+            throw new RuntimeException(MessageFormat.format(
+                    I18N.getString("error.cannot-create-output-dir"),
+                    outputDirectory.getAbsolutePath()));
+        }
+        if (!outputDirectory.canWrite()) {
+            throw new RuntimeException(MessageFormat.format(
+                    I18N.getString("error.cannot-write-to-output-dir"),
+                    outputDirectory.getAbsolutePath()));
+        }
+        if (!dependentTask) {
+            Log.verbose(MessageFormat.format(
+                    I18N.getString("message.creating-app-bundle"),
+                    name, outputDirectory.getAbsolutePath()));
+        }
+
+        // Create directory structure
+        File rootDirectory = new File(outputDirectory, name);
+
+        if (rootDirectory.exists()) {
+            if (!(OVERWRITE.fetchFrom(p))) {
+                throw new PackagerException("error.root-exists-without-overwrite",
+                        rootDirectory.getAbsolutePath());
+            }
+            try {
+                IOUtils.deleteRecursive(rootDirectory);
+            } catch (IOException ioe) {
+                throw new PackagerException(ioe);
+            }
+        }
+        rootDirectory.mkdirs();
+
+        return rootDirectory;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ArgAction.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jpackage.internal;
+
+@FunctionalInterface
+interface ArgAction {
+    void execute();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Arguments.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,866 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.jpackage.internal;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Properties;
+import java.util.ResourceBundle;
+import java.util.jar.Attributes;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
+import java.util.stream.Stream;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Arguments
+ *
+ * This class encapsulates and processes the command line arguments,
+ * in effect, implementing all the work of jpackage tool.
+ *
+ * The primary entry point, processArguments():
+ * Processes and validates command line arguments, constructing DeployParams.
+ * Validates the DeployParams, and generate the BundleParams.
+ * Generates List of Bundlers from BundleParams valid for this platform.
+ * Executes each Bundler in the list.
+ */
+public class Arguments {
+    private static final ResourceBundle I18N = ResourceBundle.getBundle(
+            "jdk.jpackage.internal.resources.MainResources");
+
+    private static final String IMAGE_MODE = "image";
+    private static final String INSTALLER_MODE = "installer";
+
+    private static final String FA_EXTENSIONS = "extension";
+    private static final String FA_CONTENT_TYPE = "mime-type";
+    private static final String FA_DESCRIPTION = "description";
+    private static final String FA_ICON = "icon";
+
+    public static final BundlerParamInfo<Boolean> CREATE_IMAGE =
+            new StandardBundlerParam<>(
+                    I18N.getString("param.create-image.name"),
+                    I18N.getString("param.create-image.description"),
+                    IMAGE_MODE,
+                    Boolean.class,
+                    p -> Boolean.FALSE,
+                    (s, p) -> (s == null || "null".equalsIgnoreCase(s)) ?
+                            true : Boolean.valueOf(s));
+
+    public static final BundlerParamInfo<Boolean> CREATE_INSTALLER =
+            new StandardBundlerParam<>(
+                    I18N.getString("param.create-installer.name"),
+                    I18N.getString("param.create-installer.description"),
+                    INSTALLER_MODE,
+                    Boolean.class,
+                    p -> Boolean.FALSE,
+                    (s, p) -> (s == null || "null".equalsIgnoreCase(s)) ?
+                            true : Boolean.valueOf(s));
+
+    // regexp for parsing args (for example, for secondary launchers)
+    private static Pattern pattern = Pattern.compile(
+          "(?:(?:([\"'])(?:\\\\\\1|.)*?(?:\\1|$))|(?:\\\\[\"'\\s]|[^\\s]))++");
+
+    private DeployParams deployParams = null;
+    private BundlerType bundleType = null;
+
+    private int pos = 0;
+    private List<String> argList = null;
+
+    private List<CLIOptions> allOptions = null;
+
+    private ArrayList<String> files = null;
+
+    private String input = null;
+    private String output = null;
+
+    private boolean hasMainJar = false;
+    private boolean hasMainClass = false;
+    private boolean hasMainModule = false;
+    private boolean hasTargetFormat = false;
+    private boolean hasAppImage = false;
+    public boolean userProvidedBuildRoot = false;
+
+    private String buildRoot = null;
+    private String mainJarPath = null;
+
+    private static boolean runtimeInstaller = false;
+
+    private List<jdk.jpackage.internal.Bundler> platformBundlers = null;
+
+    private List<SecondaryLauncherArguments> secondaryLaunchers = null;
+
+    private static Map<String, CLIOptions> argIds = new HashMap<>();
+    private static Map<String, CLIOptions> argShortIds = new HashMap<>();
+
+    {
+        // init maps for parsing arguments
+        EnumSet<CLIOptions> options = EnumSet.allOf(CLIOptions.class);
+
+        options.forEach(option -> {
+            argIds.put(option.getIdWithPrefix(), option);
+            if (option.getShortIdWithPrefix() != null) {
+                argShortIds.put(option.getShortIdWithPrefix(), option);
+            }
+        });
+    }
+
+    public Arguments(String[] args) throws PackagerException {
+        initArgumentList(args);
+    }
+
+    // CLIOptions is public for DeployParamsTest
+    public enum CLIOptions {
+        CREATE_IMAGE(IMAGE_MODE, OptionCategories.MODE, () -> {
+            context().bundleType = BundlerType.IMAGE;
+            context().deployParams.setTargetFormat("image");
+            setOptionValue(IMAGE_MODE, true);
+        }),
+
+        CREATE_INSTALLER(INSTALLER_MODE, OptionCategories.MODE, () -> {
+            setOptionValue(INSTALLER_MODE, true);
+            context().bundleType = BundlerType.INSTALLER;
+            String format = "installer";
+            context().deployParams.setTargetFormat(format);
+        }),
+
+        RUNTIME_INSTALLER("runtime-installer",
+                OptionCategories.PROPERTY, () -> {
+            runtimeInstaller = true;
+            setOptionValue("runtime-installer", true);
+        }),
+
+        INSTALLER_TYPE("installer-type", OptionCategories.PROPERTY, () -> {
+            String type = popArg();
+            if (BundlerType.INSTALLER.equals(context().bundleType)) {
+                context().deployParams.setTargetFormat(type);
+                context().hasTargetFormat = true;
+            }
+            setOptionValue("installer-type", type);
+        }),
+
+        INPUT ("input", "i", OptionCategories.PROPERTY, () -> {
+            context().input = popArg();
+            setOptionValue("input", context().input);
+        }),
+
+        OUTPUT ("output", "o", OptionCategories.PROPERTY, () -> {
+            context().output = popArg();
+            context().deployParams.setOutput(new File(context().output));
+        }),
+
+        DESCRIPTION ("description", "d", OptionCategories.PROPERTY),
+
+        VENDOR ("vendor", OptionCategories.PROPERTY),
+
+        APPCLASS ("main-class", "c", OptionCategories.PROPERTY, () -> {
+            context().hasMainClass = true;
+            setOptionValue("main-class", popArg());
+        }),
+
+        NAME ("name", "n", OptionCategories.PROPERTY),
+
+        IDENTIFIER ("identifier", OptionCategories.PROPERTY),
+
+        VERBOSE ("verbose", OptionCategories.PROPERTY, () -> {
+            setOptionValue("verbose", true);
+            Log.setVerbose(true);
+        }),
+
+        OVERWRITE ("overwrite", OptionCategories.PROPERTY, () -> {
+            setOptionValue("overwrite", true);
+        }),
+
+        RESOURCE_DIR("resource-dir",
+                OptionCategories.PROPERTY, () -> {
+            String resourceDir = popArg();
+            setOptionValue("resource-dir", resourceDir);
+        }),
+
+        FILES ("files", "f", OptionCategories.PROPERTY, () -> {
+              context().files = new ArrayList<>();
+              String files = popArg();
+              context().files.addAll(
+                      Arrays.asList(files.split(File.pathSeparator)));
+        }),
+
+        ARGUMENTS ("arguments", "a", OptionCategories.PROPERTY, () -> {
+            List<String> arguments = getArgumentList(popArg());
+            setOptionValue("arguments", arguments);
+        }),
+
+        STRIP_NATIVE_COMMANDS ("strip-native-commands",
+                   OptionCategories.PROPERTY, () -> {
+            setOptionValue("strip-native-commands", true);
+        }),
+
+        ICON ("icon", OptionCategories.PROPERTY),
+        CATEGORY ("category", OptionCategories.PROPERTY),
+        COPYRIGHT ("copyright", OptionCategories.PROPERTY),
+
+        LICENSE_FILE ("license-file", OptionCategories.PROPERTY),
+
+        VERSION ("app-version", OptionCategories.PROPERTY),
+
+        JVM_ARGS ("jvm-args", OptionCategories.PROPERTY, () -> {
+            List<String> args = getArgumentList(popArg());
+            args.forEach(a -> setOptionValue("jvm-args", a));
+        }),
+
+        FILE_ASSOCIATIONS ("file-associations",
+                OptionCategories.PROPERTY, () -> {
+            Map<String, ? super Object> args = new HashMap<>();
+
+            // load .properties file
+            Map<String, String> initialMap = getPropertiesFromFile(popArg());
+
+            String ext = initialMap.get(FA_EXTENSIONS);
+            if (ext != null) {
+                args.put(StandardBundlerParam.FA_EXTENSIONS.getID(), ext);
+            }
+
+            String type = initialMap.get(FA_CONTENT_TYPE);
+            if (type != null) {
+                args.put(StandardBundlerParam.FA_CONTENT_TYPE.getID(), type);
+            }
+
+            String desc = initialMap.get(FA_DESCRIPTION);
+            if (desc != null) {
+                args.put(StandardBundlerParam.FA_DESCRIPTION.getID(), desc);
+            }
+
+            String icon = initialMap.get(FA_ICON);
+            if (icon != null) {
+                args.put(StandardBundlerParam.FA_ICON.getID(), icon);
+            }
+
+            ArrayList<Map<String, ? super Object>> associationList =
+                new ArrayList<Map<String, ? super Object>>();
+
+            associationList.add(args);
+
+            // check that we really add _another_ value to the list
+            setOptionValue("file-associations", associationList);
+
+        }),
+
+        SECONDARY_LAUNCHER ("secondary-launcher",
+                    OptionCategories.PROPERTY, () -> {
+            context().secondaryLaunchers.add(
+                new SecondaryLauncherArguments(popArg()));
+        }),
+
+        BUILD_ROOT ("build-root", OptionCategories.PROPERTY, () -> {
+            context().buildRoot = popArg();
+            context().userProvidedBuildRoot = true;
+            setOptionValue("build-root", context().buildRoot);
+        }),
+
+        INSTALL_DIR ("install-dir", OptionCategories.PROPERTY),
+
+        PREDEFINED_APP_IMAGE ("app-image", OptionCategories.PROPERTY, ()-> {
+            setOptionValue("app-image", popArg());
+            context().hasAppImage = true;
+        }),
+
+        PREDEFINED_RUNTIME_IMAGE ("runtime-image", OptionCategories.PROPERTY),
+
+        MAIN_JAR ("main-jar", "j", OptionCategories.PROPERTY, () -> {
+            context().mainJarPath = popArg();
+            context().hasMainJar = true;
+            setOptionValue("main-jar", context().mainJarPath);
+        }),
+
+        MODULE ("module", "m", OptionCategories.MODULAR, () -> {
+            context().hasMainModule = true;
+            setOptionValue("module", popArg());
+        }),
+
+        ADD_MODULES ("add-modules", OptionCategories.MODULAR),
+
+        MODULE_PATH ("module-path", "p", OptionCategories.MODULAR),
+
+        MAC_SIGN ("mac-sign", "s", OptionCategories.PLATFORM_MAC, () -> {
+            setOptionValue("mac-sign", true);
+        }),
+
+        MAC_BUNDLE_NAME ("mac-bundle-name", OptionCategories.PLATFORM_MAC),
+
+        MAC_BUNDLE_IDENTIFIER("mac-bundle-identifier",
+                    OptionCategories.PLATFORM_MAC),
+
+        MAC_APP_STORE_CATEGORY ("mac-app-store-category",
+                    OptionCategories.PLATFORM_MAC),
+
+        MAC_BUNDLE_SIGNING_PREFIX ("mac-bundle-signing-prefix",
+                    OptionCategories.PLATFORM_MAC),
+
+        MAC_SIGNING_KEY_NAME ("mac-signing-key-user-name",
+                    OptionCategories.PLATFORM_MAC),
+
+        MAC_SIGNING_KEYCHAIN ("mac-signing-keychain",
+                    OptionCategories.PLATFORM_MAC),
+
+        MAC_APP_STORE_ENTITLEMENTS ("mac-app-store-entitlements",
+                    OptionCategories.PLATFORM_MAC),
+
+        WIN_MENU_HINT ("win-menu", OptionCategories.PLATFORM_WIN, () -> {
+            setOptionValue("win-menu", true);
+        }),
+
+        WIN_MENU_GROUP ("win-menu-group", OptionCategories.PLATFORM_WIN),
+
+        WIN_SHORTCUT_HINT ("win-shortcut",
+                OptionCategories.PLATFORM_WIN, () -> {
+            setOptionValue("win-shortcut", true);
+        }),
+
+        WIN_PER_USER_INSTALLATION ("win-per-user-install",
+                OptionCategories.PLATFORM_WIN, () -> {
+            setOptionValue("win-per-user-install", false);
+        }),
+
+        WIN_DIR_CHOOSER ("win-dir-chooser",
+                OptionCategories.PLATFORM_WIN, () -> {
+            setOptionValue("win-dir-chooser", true);
+        }),
+
+        WIN_REGISTRY_NAME ("win-registry-name", OptionCategories.PLATFORM_WIN),
+
+        WIN_UPGRADE_UUID ("win-upgrade-uuid",
+                OptionCategories.PLATFORM_WIN),
+
+        WIN_CONSOLE_HINT ("win-console", OptionCategories.PLATFORM_WIN, () -> {
+            setOptionValue("win-console", true);
+        }),
+
+        LINUX_BUNDLE_NAME ("linux-bundle-name",
+                OptionCategories.PLATFORM_LINUX),
+
+        LINUX_DEB_MAINTAINER ("linux-deb-maintainer",
+                OptionCategories.PLATFORM_LINUX),
+
+        LINUX_RPM_LICENSE_TYPE ("linux-rpm-license-type",
+                OptionCategories.PLATFORM_LINUX),
+
+        LINUX_PACKAGE_DEPENDENCIES ("linux-package-deps",
+                OptionCategories.PLATFORM_LINUX);
+
+        private final String id;
+        private final String shortId;
+        private final OptionCategories category;
+        private final ArgAction action;
+        private static Arguments argContext;
+
+        private CLIOptions(String id, OptionCategories category) {
+            this(id, null, category, null);
+        }
+
+        private CLIOptions(String id, String shortId,
+                           OptionCategories category) {
+            this(id, shortId, category, null);
+        }
+
+        private CLIOptions(String id,
+                OptionCategories category, ArgAction action) {
+            this(id, null, category, action);
+        }
+
+        private CLIOptions(String id, String shortId,
+                           OptionCategories category, ArgAction action) {
+            this.id = id;
+            this.shortId = shortId;
+            this.action = action;
+            this.category = category;
+        }
+
+        static void setContext(Arguments context) {
+            argContext = context;
+        }
+
+        public static Arguments context() {
+            if (argContext != null) {
+                return argContext;
+            } else {
+                throw new RuntimeException("Argument context is not set.");
+            }
+        }
+
+        public String getId() {
+            return this.id;
+        }
+
+        String getIdWithPrefix() {
+            String prefix = isMode() ? "create-" : "--";
+            return prefix + this.id;
+        }
+
+        String getShortIdWithPrefix() {
+            return this.shortId == null ? null : "-" + this.shortId;
+        }
+
+        void execute() {
+            if (action != null) {
+                action.execute();
+            } else {
+                defaultAction();
+            }
+        }
+
+        boolean isMode() {
+            return category == OptionCategories.MODE;
+        }
+
+        OptionCategories getCategory() {
+            return category;
+        }
+
+        private void defaultAction() {
+            context().deployParams.addBundleArgument(id, popArg());
+        }
+
+        private static void setOptionValue(String option, Object value) {
+            context().deployParams.addBundleArgument(option, value);
+        }
+
+        private static String popArg() {
+            nextArg();
+            return (context().pos >= context().argList.size()) ?
+                            "" : context().argList.get(context().pos);
+        }
+
+        private static String getArg() {
+            return (context().pos >= context().argList.size()) ?
+                        "" : context().argList.get(context().pos);
+        }
+
+        private static void nextArg() {
+            context().pos++;
+        }
+
+        private static void prevArg() {
+            context().pos--;
+        }
+
+        private static boolean hasNextArg() {
+            return context().pos < context().argList.size();
+        }
+    }
+
+    enum OptionCategories {
+        MODE,
+        MODULAR,
+        PROPERTY,
+        PLATFORM_MAC,
+        PLATFORM_WIN,
+        PLATFORM_LINUX;
+    }
+
+    private void initArgumentList(String[] args) throws PackagerException {
+        argList = new ArrayList<String>(args.length);
+        for (String arg : args) {
+            argList.add(arg);
+        }
+        Log.debug ("\njpackage argument list: \n" + argList + "\n");
+        pos = 0;
+
+        deployParams = new DeployParams();
+        bundleType = BundlerType.NONE;
+
+        allOptions = new ArrayList<>();
+
+        secondaryLaunchers = new ArrayList<>();
+    }
+
+    public boolean processArguments() throws Exception {
+        try {
+
+            // init context of arguments
+            CLIOptions.setContext(this);
+
+            // parse cmd line
+            String arg;
+            CLIOptions option;
+            for (; CLIOptions.hasNextArg(); CLIOptions.nextArg()) {
+                arg = CLIOptions.getArg();
+                if ((option = toCLIOption(arg)) != null) {
+                    // found a CLI option
+                    allOptions.add(option);
+                    option.execute();
+                } else {
+                    throw new PackagerException("ERR_InvalidOption", arg);
+                }
+            }
+
+            if (allOptions.isEmpty() || !allOptions.get(0).isMode()) {
+                // first argument should always be a mode.
+                throw new PackagerException("ERR_MissingMode");
+            }
+
+            if (hasMainJar && !hasMainClass) {
+                // try to get main-class from manifest
+                String mainClass = getMainClassFromManifest();
+                if (mainClass != null) {
+                    CLIOptions.setOptionValue(
+                            CLIOptions.APPCLASS.getId(), mainClass);
+                }
+            }
+
+            // display warning for arguments that are not supported
+            // for current configuration.
+
+            validateArguments();
+
+            addResources(deployParams, input, files);
+
+            deployParams.setBundleType(bundleType);
+
+            List<Map<String, ? super Object>> launchersAsMap =
+                    new ArrayList<>();
+
+            for (SecondaryLauncherArguments sl : secondaryLaunchers) {
+                launchersAsMap.add(sl.getLauncherMap());
+            }
+
+            deployParams.addBundleArgument(
+                    StandardBundlerParam.SECONDARY_LAUNCHERS.getID(),
+                    launchersAsMap);
+
+            // at this point deployParams should be already configured
+
+            deployParams.validate();
+
+            BundleParams bp = deployParams.getBundleParams();
+
+            // validate name(s)
+            ArrayList<String> usedNames = new ArrayList<String>();
+            usedNames.add(bp.getName()); // add main app name
+
+            for (SecondaryLauncherArguments sl : secondaryLaunchers) {
+                Map<String, ? super Object> slMap = sl.getLauncherMap();
+                String slName =
+                        (String) slMap.get(Arguments.CLIOptions.NAME.getId());
+                if (slName == null) {
+                    throw new PackagerException("ERR_NoSecondaryLauncherName");
+                }
+                // same rules apply to secondary launcher names as app name
+                DeployParams.validateName(slName, false);
+                for (String usedName : usedNames) {
+                    if (slName.equals(usedName)) {
+                        throw new PackagerException("ERR_NoUniqueName");
+                    }
+                }
+                usedNames.add(slName);
+            }
+            if (runtimeInstaller && bp.getName() == null) {
+                throw new PackagerException("ERR_NoJreInstallerName");
+            }
+
+            return generateBundle(bp.getBundleParamsAsMap());
+        } catch (Exception e) {
+            if (Log.isVerbose()) {
+                throw e;
+            } else {
+                Log.error(e.getMessage());
+                if (e.getCause() != null && e.getCause() != e) {
+                    Log.error(e.getCause().getMessage());
+                }
+                return false;
+            }
+        }
+    }
+
+    private void validateArguments() {
+        CLIOptions mode = allOptions.get(0);
+        for (CLIOptions option : allOptions) {
+            if(!ValidOptions.checkIfSupported(mode, option)) {
+                String key = "warning.unsupported.option";
+                if (ValidOptions.checkIfOtherSupported(mode, option)) {
+                    key = "warning.unsupported.mode.option";
+                }
+                Log.info(MessageFormat.format(I18N.getString(key),
+                        option.getId(), mode));
+            }
+        }
+    }
+
+    private List<jdk.jpackage.internal.Bundler> getPlatformBundlers() {
+
+        if (platformBundlers != null) {
+            return platformBundlers;
+        }
+
+        platformBundlers = new ArrayList<>();
+        for (jdk.jpackage.internal.Bundler bundler :
+                Bundlers.createBundlersInstance().getBundlers(
+                        bundleType.toString())) {
+            if (hasTargetFormat && deployParams.getTargetFormat() != null &&
+                    !deployParams.getTargetFormat().equalsIgnoreCase(
+                    bundler.getID())) {
+                continue;
+            }
+            if (bundler.supported(runtimeInstaller)) {
+                 platformBundlers.add(bundler);
+            }
+        }
+
+        return platformBundlers;
+    }
+
+    private boolean generateBundle(Map<String,? super Object> params)
+            throws PackagerException {
+
+        boolean bundleCreated = false;
+
+        // the build-root needs to be fetched from the params early,
+        // to prevent each copy of the params (such as may be used for
+        // secondary launchers) from generating a separate build-root when
+        // the default is used (the default is a new temp directory)
+        // The bundler.cleanup() below would not otherwise be able to
+        // clean these extra (and unneeded) temp directories.
+        StandardBundlerParam.BUILD_ROOT.fetchFrom(params);
+
+        for (jdk.jpackage.internal.Bundler bundler : getPlatformBundlers()) {
+            Map<String, ? super Object> localParams = new HashMap<>(params);
+            try {
+                if (bundler.validate(localParams)) {
+                    File result =
+                            bundler.execute(localParams, deployParams.outdir);
+                    if (!userProvidedBuildRoot) {
+                        bundler.cleanup(localParams);
+                    }
+                    if (result == null) {
+                        throw new PackagerException("MSG_BundlerFailed",
+                                bundler.getID(), bundler.getName());
+                    }
+                    bundleCreated = true; // at least one bundle was created
+                }
+            } catch (UnsupportedPlatformException e) {
+                throw new PackagerException(e,
+                        "MSG_BundlerPlatformException",
+                        bundler.getName());
+            } catch (ConfigException e) {
+                Log.debug(e);
+                if (e.getAdvice() != null) {
+                    throw new PackagerException(e,
+                            "MSG_BundlerConfigException",
+                            bundler.getName(), e.getMessage(), e.getAdvice());
+                } else {
+                    throw new PackagerException(e,
+                           "MSG_BundlerConfigExceptionNoAdvice",
+                            bundler.getName(), e.getMessage());
+                }
+            } catch (RuntimeException re) {
+                Log.debug(re);
+                throw new PackagerException(re, "MSG_BundlerRuntimeException",
+                        bundler.getName(), re.toString());
+            } finally {
+                if (userProvidedBuildRoot) {
+                    Log.verbose(MessageFormat.format(
+                            I18N.getString("message.debug-working-directory"),
+                            (new File(buildRoot)).getAbsolutePath()));
+                }
+            }
+        }
+
+        return bundleCreated;
+    }
+
+    private void addResources(DeployParams deployParams,
+            String inputdir, List<String> inputfiles) {
+
+        if (inputdir == null || inputdir.isEmpty()) {
+            return;
+        }
+
+        File baseDir = new File(inputdir);
+
+        if (!baseDir.isDirectory()) {
+            Log.error(
+                    "Unable to add resources: \"--input\" is not a directory.");
+            return;
+        }
+
+        List<String> fileNames;
+        if (inputfiles != null) {
+            fileNames = inputfiles;
+        } else {
+            // "-files" is omitted, all files in input cdir (which
+            // is a mandatory argument in this case) will be packaged.
+            fileNames = new ArrayList<>();
+            try (Stream<Path> files = Files.list(baseDir.toPath())) {
+                files.forEach(file -> fileNames.add(
+                        file.getFileName().toString()));
+            } catch (IOException e) {
+                Log.error("Unable to add resources: " + e.getMessage());
+            }
+        }
+        fileNames.forEach(file -> deployParams.addResource(baseDir, file));
+
+        deployParams.setClasspath();
+    }
+
+    static boolean isCLIOption(String arg) {
+        return toCLIOption(arg) != null;
+    }
+
+    static CLIOptions toCLIOption(String arg) {
+        CLIOptions option;
+        if ((option = argIds.get(arg)) == null) {
+            option = argShortIds.get(arg);
+        }
+        return option;
+    }
+
+    static Map<String, String> getArgumentMap(String inputString) {
+        Map<String, String> map = new HashMap<>();
+        List<String> list = getArgumentList(inputString);
+        for (String pair : list) {
+            int equals = pair.indexOf("=");
+            if (equals != -1) {
+                String key = pair.substring(0, equals);
+                String value = pair.substring(equals+1, pair.length());
+                map.put(key, value);
+            }
+        }
+        return map;
+    }
+
+    static Map<String, String> getPropertiesFromFile(String filename) {
+        Map<String, String> map = new HashMap<>();
+        // load properties file
+        File file = new File(filename);
+        Properties properties = new Properties();
+        try (FileInputStream in = new FileInputStream(file)) {
+            properties.load(in);
+        } catch (IOException e) {
+            Log.error("Exception: " + e.getMessage());
+        }
+
+        for (final String name: properties.stringPropertyNames()) {
+            map.put(name, properties.getProperty(name));
+        }
+
+        return map;
+    }
+
+    static List<String> getArgumentList(String inputString) {
+        List<String> list = new ArrayList<>();
+        if (inputString == null || inputString.isEmpty()) {
+             return list;
+        }
+
+        // The "pattern" regexp attempts to abide to the rule that
+        // strings are delimited by whitespace unless surrounded by
+        // quotes, then it is anything (including spaces) in the quotes.
+        Matcher m = pattern.matcher(inputString);
+        while (m.find()) {
+            String s = inputString.substring(m.start(), m.end()).trim();
+            // Ensure we do not have an empty string. trim() will take care of
+            // whitespace only strings. The regex preserves quotes and escaped
+            // chars so we need to clean them before adding to the List
+            if (!s.isEmpty()) {
+                list.add(unquoteIfNeeded(s));
+            }
+        }
+        return list;
+    }
+
+    private static String unquoteIfNeeded(String in) {
+        if (in == null) {
+            return null;
+        }
+
+        if (in.isEmpty()) {
+            return "";
+        }
+
+        // Use code points to preserve non-ASCII chars
+        StringBuilder sb = new StringBuilder();
+        int codeLen = in.codePointCount(0, in.length());
+        int quoteChar = -1;
+        for (int i = 0; i < codeLen; i++) {
+            int code = in.codePointAt(i);
+            if (code == '"' || code == '\'') {
+                // If quote is escaped make sure to copy it
+                if (i > 0 && in.codePointAt(i - 1) == '\\') {
+                    sb.deleteCharAt(sb.length() - 1);
+                    sb.appendCodePoint(code);
+                    continue;
+                }
+                if (quoteChar != -1) {
+                    if (code == quoteChar) {
+                        // close quote, skip char
+                        quoteChar = -1;
+                    } else {
+                        sb.appendCodePoint(code);
+                    }
+                } else {
+                    // opening quote, skip char
+                    quoteChar = code;
+                }
+            } else {
+                sb.appendCodePoint(code);
+            }
+        }
+        return sb.toString();
+    }
+
+    private String getMainClassFromManifest() {
+        if (mainJarPath == null ||
+            input == null ) {
+            return null;
+        }
+
+        JarFile jf;
+        try {
+            File file = new File(input, mainJarPath);
+            if (!file.exists()) {
+                return null;
+            }
+            jf = new JarFile(file);
+            Manifest m = jf.getManifest();
+            Attributes attrs = (m != null) ? m.getMainAttributes() : null;
+            if (attrs != null) {
+                return attrs.getValue(Attributes.Name.MAIN_CLASS);
+            }
+        } catch (IOException ignore) {}
+        return null;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/BasicBundlers.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2014, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jpackage.internal;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.ServiceLoader;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * BasicBundlers
+ *
+ * A basic bundlers collection that loads the default bundlers.
+ * Loads the common bundlers.
+ * <UL>
+ *     <LI>Windows file image</LI>
+ *     <LI>Mac .app</LI>
+ *     <LI>Linux file image</LI>
+ *     <LI>Windows MSI</LI>
+ *     <LI>Windows EXE</LI>
+ *     <LI>Mac DMG</LI>
+ *     <LI>Mac PKG</LI>
+ *     <LI>Linux DEB</LI>
+ *     <LI>Linux RPM</LI>
+ *
+ * </UL>
+ */
+public class BasicBundlers implements Bundlers {
+
+    boolean defaultsLoaded = false;
+
+    private final Collection<Bundler> bundlers = new CopyOnWriteArrayList<>();
+
+    @Override
+    public Collection<Bundler> getBundlers() {
+        return Collections.unmodifiableCollection(bundlers);
+    }
+
+    @Override
+    public Collection<Bundler> getBundlers(String type) {
+        if (type == null) return Collections.emptySet();
+        switch (type) {
+            case "NONE":
+                return Collections.emptySet();
+            case "ALL":
+                return getBundlers();
+            default:
+                return Arrays.asList(getBundlers().stream()
+                        .filter(b -> type.equalsIgnoreCase(b.getBundleType()))
+                        .toArray(Bundler[]::new));
+        }
+    }
+
+    @Override
+    public void loadDefaultBundlers() {
+        // no-op.  We now load all bundlers from module system.
+    }
+
+    // Loads bundlers from the META-INF/services direct
+    @Override
+    public void loadBundlersFromServices(ClassLoader cl) {
+        ServiceLoader<Bundler> loader = ServiceLoader.load(Bundler.class, cl);
+        for (Bundler aLoader : loader) {
+            bundlers.add(aLoader);
+        }
+    }
+
+    @Override
+    public void loadBundler(Bundler bundler) {
+        bundlers.add(bundler);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/BundleParams.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,410 @@
+/*
+ * Copyright (c) 2012, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jpackage.internal;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.*;
+import java.util.jar.Attributes;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
+
+import static jdk.jpackage.internal.StandardBundlerParam.*;
+
+public class BundleParams {
+
+    final protected Map<String, ? super Object> params;
+
+    // RelativeFileSet
+    public static final String PARAM_APP_RESOURCES      = "appResources";
+
+    // BundlerType
+    public static final String PARAM_TYPE               = "type";
+
+    // String
+    public static final String PARAM_BUNDLE_FORMAT      = "bundleFormat";
+    // String
+    public static final String PARAM_ICON               = "icon";
+
+    // String - Name of bundle file and native launcher
+    public static final String PARAM_NAME               = "name";
+
+    // String - application vendor, used by most of the bundlers
+    public static final String PARAM_VENDOR             = "vendor";
+
+    // String - email name and email, only used for debian */
+    public static final String PARAM_EMAIL              = "email";
+
+    /* String - Copyright. Used on Mac */
+    public static final String PARAM_COPYRIGHT          = "copyright";
+
+    // String - GUID on windows for MSI, CFBundleIdentifier on Mac
+    // If not compatible with requirements then bundler either do not bundle
+    // or autogenerate
+    public static final String PARAM_IDENTIFIER         = "identifier";
+
+    /* boolean - shortcut preferences */
+    public static final String PARAM_SHORTCUT           = "shortcutHint";
+    // boolean - menu shortcut preference
+    public static final String PARAM_MENU               = "menuHint";
+
+    // String - Application version. Format may differ for different bundlers
+    public static final String PARAM_VERSION            = "appVersion";
+
+    // String - Application category. Used at least on Mac/Linux.
+    // Value is platform specific
+    public static final String PARAM_CATEGORY       = "applicationCategory";
+
+    // String - Optional short application
+    public static final String PARAM_TITLE              = "title";
+
+    // String - Optional application description. Used by MSI and on Linux
+    public static final String PARAM_DESCRIPTION        = "description";
+
+    // String - License type. Needed on Linux (rpm)
+    public static final String PARAM_LICENSE_TYPE       = "licenseType";
+
+    // String - File with license. Format is OS/bundler specific
+    public static final String PARAM_LICENSE_FILE       = "licenseFile";
+
+    // String Main application class.
+    // Not used directly but used to derive default values
+    public static final String PARAM_APPLICATION_CLASS  = "applicationClass";
+
+    // boolean - Adds a dialog to let the user choose a directory
+    // where the product will be installed.
+    public static final String PARAM_INSTALLDIR_CHOOSER = "installdirChooser";
+
+    /**
+     * create a new bundle with all default values
+     */
+    public BundleParams() {
+        params = new HashMap<>();
+    }
+
+    /**
+     * Create a bundle params with a copy of the params
+     * @param params map of initial parameters to be copied in.
+     */
+    public BundleParams(Map<String, ?> params) {
+        this.params = new HashMap<>(params);
+    }
+
+    public void addAllBundleParams(Map<String, ? super Object> p) {
+        params.putAll(p);
+    }
+
+    public <C> C fetchParam(BundlerParamInfo<C> paramInfo) {
+        return paramInfo.fetchFrom(params);
+    }
+
+    @SuppressWarnings("unchecked")
+    public <C> C fetchParamWithDefault(
+            Class<C> klass, C defaultValue, String... keys) {
+        for (String key : keys) {
+            Object o = params.get(key);
+            if (klass.isInstance(o)) {
+                return (C) o;
+            } else if (params.containsKey(key) && o == null) {
+                return null;
+            } else if (o != null) {
+                Log.debug("Bundle param " + key + " is not type " + klass);
+            }
+        }
+        return defaultValue;
+    }
+
+    public <C> C fetchParam(Class<C> klass, String... keys) {
+        return fetchParamWithDefault(klass, null, keys);
+    }
+
+    // NOTE: we do not care about application parameters here
+    // as they will be embeded into jar file manifest and
+    // java launcher will take care of them!
+
+    public Map<String, ? super Object> getBundleParamsAsMap() {
+        return new HashMap<>(params);
+    }
+
+    public void setJvmargs(List<String> jvmargs) {
+        putUnlessNullOrEmpty(JVM_OPTIONS.getID(), jvmargs);
+    }
+
+    public void setArguments(List<String> arguments) {
+        putUnlessNullOrEmpty(ARGUMENTS.getID(), arguments);
+    }
+
+    public void setAddModules(String value) {
+        putUnlessNull(StandardBundlerParam.ADD_MODULES.getID(), value);
+    }
+
+    public void setLimitModules(String value)  {
+        putUnlessNull(StandardBundlerParam.LIMIT_MODULES.getID(), value);
+    }
+
+    public void setStripNativeCommands(boolean value) {
+        putUnlessNull(StandardBundlerParam.STRIP_NATIVE_COMMANDS.getID(),
+                value);
+    }
+
+    public void setModulePath(String value) {
+        putUnlessNull(StandardBundlerParam.MODULE_PATH.getID(), value);
+    }
+
+    public void setMainModule(String value) {
+        putUnlessNull(StandardBundlerParam.MODULE.getID(), value);
+    }
+
+    public void setDebug(String value) {
+        putUnlessNull(JLinkBundlerHelper.DEBUG.getID(), value);
+    }
+
+    public String getApplicationID() {
+        return fetchParam(IDENTIFIER);
+    }
+
+    public String getPreferencesID() {
+        return fetchParam(PREFERENCES_ID);
+    }
+
+    public String getTitle() {
+        return fetchParam(TITLE);
+    }
+
+    public void setTitle(String title) {
+        putUnlessNull(PARAM_TITLE, title);
+    }
+
+    public String getApplicationClass() {
+        return fetchParam(MAIN_CLASS);
+    }
+
+    public void setApplicationClass(String applicationClass) {
+        putUnlessNull(PARAM_APPLICATION_CLASS, applicationClass);
+    }
+
+    public String getAppVersion() {
+        return fetchParam(VERSION);
+    }
+
+    public void setAppVersion(String version) {
+        putUnlessNull(PARAM_VERSION, version);
+    }
+
+    public String getDescription() {
+        return fetchParam(DESCRIPTION);
+    }
+
+    public void setDescription(String s) {
+        putUnlessNull(PARAM_DESCRIPTION, s);
+    }
+
+    public void setInstalldirChooser(Boolean b) {
+        putUnlessNull(PARAM_INSTALLDIR_CHOOSER, b);
+    }
+
+    public String getName() {
+        return fetchParam(APP_NAME);
+    }
+
+    public void setName(String name) {
+        putUnlessNull(PARAM_NAME, name);
+    }
+
+    @SuppressWarnings("deprecation")
+    public BundlerType getType() {
+        return fetchParam(BundlerType.class, PARAM_TYPE);
+    }
+
+    @SuppressWarnings("deprecation")
+    public void setType(BundlerType type) {
+        putUnlessNull(PARAM_TYPE, type);
+    }
+
+    public String getBundleFormat() {
+        return fetchParam(String.class, PARAM_BUNDLE_FORMAT);
+    }
+
+    public void setBundleFormat(String t) {
+        putUnlessNull(PARAM_BUNDLE_FORMAT, t);
+    }
+
+    public boolean getVerbose() {
+        return fetchParam(VERBOSE);
+    }
+
+    public List<String> getJvmargs() {
+        return JVM_OPTIONS.fetchFrom(params);
+    }
+
+    public List<String> getArguments() {
+        return ARGUMENTS.fetchFrom(params);
+    }
+
+    public jdk.jpackage.internal.RelativeFileSet getAppResource() {
+        return fetchParam(APP_RESOURCES);
+    }
+
+    public void setAppResource(jdk.jpackage.internal.RelativeFileSet fs) {
+        putUnlessNull(PARAM_APP_RESOURCES, fs);
+    }
+
+    public void setAppResourcesList(
+            List<jdk.jpackage.internal.RelativeFileSet> rfs) {
+        putUnlessNull(APP_RESOURCES_LIST.getID(), rfs);
+    }
+
+    public String getApplicationCategory() {
+        return fetchParam(CATEGORY);
+    }
+
+    public void setApplicationCategory(String category) {
+        putUnlessNull(PARAM_CATEGORY, category);
+    }
+
+    public String getMainClassName() {
+        String applicationClass = getApplicationClass();
+
+        if (applicationClass == null) {
+            return null;
+        }
+
+        int idx = applicationClass.lastIndexOf(".");
+        if (idx >= 0) {
+            return applicationClass.substring(idx+1);
+        }
+        return applicationClass;
+    }
+
+    public String getCopyright() {
+        return fetchParam(COPYRIGHT);
+    }
+
+    public void setCopyright(String c) {
+        putUnlessNull(PARAM_COPYRIGHT, c);
+    }
+
+    public String getIdentifier() {
+        return fetchParam(IDENTIFIER);
+    }
+
+    public void setIdentifier(String s) {
+        putUnlessNull(PARAM_IDENTIFIER, s);
+    }
+
+    private String mainJar = null;
+
+    // assuming that application was packaged according to the rules
+    // we must have application jar, i.e. jar where we embed launcher
+    // and have main application class listed as main class!
+    // If there are more than one, or none - it will be treated as
+    // deployment error
+    //
+    // Note we look for both JavaFX executable jars and regular executable jars
+    // As long as main "application" entry point is the same it is main class
+    // (i.e. for FX jar we will use JavaFX manifest entry ...)
+    public String getMainApplicationJar() {
+        jdk.jpackage.internal.RelativeFileSet appResources = getAppResource();
+        if (mainJar != null) {
+            if (getApplicationClass() == null) try {
+                if (appResources != null) {
+                    File srcdir = appResources.getBaseDirectory();
+                    JarFile jf = new JarFile(new File(srcdir, mainJar));
+                    Manifest m = jf.getManifest();
+                    Attributes attrs = (m != null) ?
+                            m.getMainAttributes() : null;
+                    if (attrs != null) {
+                        setApplicationClass(
+                                attrs.getValue(Attributes.Name.MAIN_CLASS));
+                    }
+                }
+            } catch (IOException ignore) {
+            }
+            return mainJar;
+        }
+
+        String applicationClass = getApplicationClass();
+
+        if (appResources == null || applicationClass == null) {
+            return null;
+        }
+        File srcdir = appResources.getBaseDirectory();
+        for (String fname : appResources.getIncludedFiles()) {
+            JarFile jf;
+            try {
+                jf = new JarFile(new File(srcdir, fname));
+                Manifest m = jf.getManifest();
+                Attributes attrs = (m != null) ? m.getMainAttributes() : null;
+                if (attrs != null) {
+                    boolean javaMain = applicationClass.equals(
+                               attrs.getValue(Attributes.Name.MAIN_CLASS));
+
+                    if (javaMain) {
+                        mainJar = fname;
+                        return mainJar;
+                    }
+                }
+            } catch (IOException ignore) {
+            }
+        }
+        return null;
+    }
+
+    public String getVendor() {
+        return fetchParam(VENDOR);
+    }
+
+    public void setVendor(String vendor) {
+       putUnlessNull(PARAM_VENDOR, vendor);
+    }
+
+    public String getEmail() {
+        return fetchParam(String.class, PARAM_EMAIL);
+    }
+
+    public void setEmail(String email) {
+        putUnlessNull(PARAM_EMAIL, email);
+    }
+
+    public void putUnlessNull(String param, Object value) {
+        if (value != null) {
+            params.put(param, value);
+        }
+    }
+
+    public void putUnlessNullOrEmpty(String param, Collection<?> value) {
+        if (value != null && !value.isEmpty()) {
+            params.put(param, value);
+        }
+    }
+
+    public void putUnlessNullOrEmpty(String param, Map<?,?> value) {
+        if (value != null && !value.isEmpty()) {
+            params.put(param, value);
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Bundler.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2014, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jpackage.internal;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * Bundler
+ *
+ * The basic interface implemented by all Bundlers.
+ */
+public interface Bundler {
+    /**
+     * @return User Friendly name of this bundler.
+     */
+    String getName();
+
+    /**
+     * @return A more verbose description of the bundler.
+     */
+    String getDescription();
+
+    /**
+     * @return Command line identifier of the bundler.  Should be unique.
+     */
+    String getID();
+
+    /**
+     * @return The bundle type of the bundle that is created by this bundler.
+     */
+    String getBundleType();
+
+    /**
+     * The parameters that this bundler uses to generate it's bundle.
+     * @return immutable collection
+     */
+    Collection<BundlerParamInfo<?>> getBundleParameters();
+
+    /**
+     * Determines if this bundler will execute with the given parameters.
+     *
+     * @param params The parameters to be validate.  Validation may modify
+     *               the map, so if you are going to be using the same map
+     *               across multiple bundlers you should pass in a deep copy.
+     * @return true if valid
+     * @throws UnsupportedPlatformException If the bundler cannot run on this
+     *         platform (i.e. creating mac apps on windows)
+     * @throws ConfigException If the configuration params are incorrect.  The
+     *         exception may contain advice on how to modify the params map
+     *         to make it valid.
+     */
+    public boolean validate(Map<String, ? super Object> params)
+            throws UnsupportedPlatformException, ConfigException;
+
+    /**
+     * Creates a bundle from existing content.
+     *
+     * If a call to {@link #validate(java.util.Map)} date} returns true with
+     * the parameters map, then you can expect a valid output.
+     * However if an exception was thrown out of validate or it returned
+     * false then you should not expect sensible results from this call.
+     * It may or may not return a value, and it may or may not throw an
+     * exception.  But any output should not be considered valid or sane.
+     *
+     * @param params The parameters as specified by getBundleParameters.
+     *               Keyed by the id from the ParamInfo.  Execution may
+     *               modify the map, so if you are going to be using the
+     *               same map across multiple bundlers you should pass
+     *               in a deep copy.
+     * @param outputParentDir
+     *   The parent dir that the returned bundle will be placed in.
+     * @return The resulting bundled file
+     *
+     * For a bundler that produces a single artifact file this will be the
+     * location of that artifact (.exe file, .deb file, etc)
+     *
+     * For a bundler that produces a specific directory format output this will
+     * be the location of that specific directory (.app file, etc).
+     *
+     * For a bundler that produce multiple files, this will be a parent
+     * directory of those files (linux and windows images), whose name is not
+     * relevant to the result.
+     *
+     * @throws java.lang.IllegalArgumentException for any of the following
+     * reasons:
+     *  <ul>
+     *      <li>A required parameter is not found in the params list, for
+     *      example missing the main class.</li>
+     *      <li>A parameter has the wrong type of an object, for example a
+     *      String where a File is required</li>
+     *      <li>Bundler specific incompatibilities with the parameters, for
+     *      example a bad version number format or an application id with
+     *      forward slashes.</li>
+     *  </ul>
+     */
+    public File execute(Map<String, ? super Object> params,
+            File outputParentDir) throws PackagerException;
+
+     /**
+     * Removes temporary files that are used for bundling.
+     */
+    public void cleanup(Map<String, ? super Object> params);
+
+    /**
+     * Returns "true" if this bundler is supported on current platform.
+     */
+    public boolean supported(boolean runtimeInstaller);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/BundlerParamInfo.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2014, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jpackage.internal;
+
+import java.util.Map;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+
+/**
+ * BundlerParamInfo<T>
+ *
+ * A BundlerParamInfo encapsulates an individual bundler parameter of type <T>.
+ */
+class BundlerParamInfo<T> {
+    /**
+     * The user friendly name of the parameter
+     */
+    String name;
+
+    /**
+     * A more verbose description of the parameter
+     */
+    String description;
+
+    /**
+     * The command line and hashmap name of the parameter
+     */
+    String id;
+
+    /**
+     * Type of the parameter
+     */
+    Class<T> valueType;
+
+    /**
+     * Indicates if value was set using default value function
+     */
+    boolean isDefaultValue;
+
+    /**
+     * If the value is not set, and no fallback value is found,
+     * the parameter uses the value returned by the producer.
+     */
+    Function<Map<String, ? super Object>, T> defaultValueFunction;
+
+    /**
+     * An optional string converter for command line arguments.
+     */
+    BiFunction<String, Map<String, ? super Object>, T> stringConverter;
+
+    String getName() {
+        return name;
+    }
+
+    void setName(String name) {
+        this.name = name;
+    }
+
+    String getDescription() {
+        return description;
+    }
+
+    void setDescription(String description) {
+        this.description = description;
+    }
+
+    String getID() {
+        return id;
+    }
+
+    Class<T> getValueType() {
+        return valueType;
+    }
+
+    void setValueType(Class<T> valueType) {
+        this.valueType = valueType;
+    }
+
+    boolean getIsDefaultValue() {
+        return isDefaultValue;
+    }
+
+    Function<Map<String, ? super Object>, T> getDefaultValueFunction() {
+        return defaultValueFunction;
+    }
+
+    void setDefaultValueFunction(
+            Function<Map<String, ? super Object>, T> defaultValueFunction) {
+        this.defaultValueFunction = defaultValueFunction;
+    }
+
+    BiFunction<String, Map<String, ? super Object>,T>
+            getStringConverter() {
+        return stringConverter;
+    }
+
+    void setStringConverter(BiFunction<String,
+            Map<String, ? super Object>, T> stringConverter) {
+        this.stringConverter = stringConverter;
+    }
+
+    @SuppressWarnings("unchecked")
+    final T fetchFrom(Map<String, ? super Object> params) {
+        return fetchFrom(params, true);
+    }
+
+    @SuppressWarnings("unchecked")
+    final T fetchFrom(Map<String, ? super Object> params,
+            boolean invokeDefault) {
+        Object o = params.get(getID());
+        if (o instanceof String && getStringConverter() != null) {
+            return getStringConverter().apply((String)o, params);
+        }
+
+        Class<T> klass = getValueType();
+        if (klass.isInstance(o)) {
+            return (T) o;
+        }
+        if (o != null) {
+            throw new IllegalArgumentException("Param " + getID()
+                    + " should be of type " + getValueType()
+                    + " but is a " + o.getClass());
+        }
+        if (params.containsKey(getID())) {
+            // explicit nulls are allowed
+            return null;
+        }
+
+        if (invokeDefault && (getDefaultValueFunction() != null)) {
+            T result =  getDefaultValueFunction().apply(params);
+            if (result != null) {
+                params.put(getID(), result);
+                isDefaultValue = true;
+            }
+            return result;
+        }
+
+        // ultimate fallback
+        return null;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/BundlerType.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2012, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jpackage.internal;
+
+enum BundlerType {
+    NONE,
+    IMAGE,    // Generates app image only
+    INSTALLER // Generates installers
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Bundlers.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2014, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jpackage.internal;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.ServiceLoader;
+
+/**
+ * Bundlers
+ *
+ * The interface implemented by BasicBundlers
+ */
+public interface Bundlers {
+
+    /**
+     * This convenience method will call
+     * {@link #createBundlersInstance(ClassLoader)}
+     * with the classloader that this Bundlers is loaded from.
+     *
+     * @return an instance of Bundlers loaded and configured from
+     *         the current ClassLoader.
+     */
+    public static Bundlers createBundlersInstance() {
+        return createBundlersInstance(Bundlers.class.getClassLoader());
+    }
+
+    /**
+     * This convenience method will automatically load a Bundlers instance
+     * from either META-INF/services or the default
+     * {@link BasicBundlers} if none are found in
+     * the services meta-inf.
+     *
+     * After instantiating the bundlers instance it will load the default
+     * bundlers via {@link #loadDefaultBundlers()} as well as requesting
+     * the services loader to load any other bundelrs via
+     * {@link #loadBundlersFromServices(ClassLoader)}.
+
+     *
+     * @param servicesClassLoader the classloader to search for
+     *                            META-INF/service registered bundlers
+     * @return an instance of Bundlers loaded and configured from
+     *         the specified ClassLoader
+     */
+    public static Bundlers createBundlersInstance(
+            ClassLoader servicesClassLoader) {
+        ServiceLoader<Bundlers> bundlersLoader =
+                ServiceLoader.load(Bundlers.class, servicesClassLoader);
+        Bundlers bundlers = null;
+        Iterator<Bundlers> iter = bundlersLoader.iterator();
+        if (iter.hasNext()) {
+            bundlers = iter.next();
+        }
+        if (bundlers == null) {
+            bundlers = new BasicBundlers();
+        }
+
+        bundlers.loadBundlersFromServices(servicesClassLoader);
+        return bundlers;
+    }
+
+    /**
+     * Returns all of the preconfigured, requested, and manually
+     * configured bundlers loaded with this instance.
+     *
+     * @return  a read-only collection of the requested bundlers
+     */
+    Collection<Bundler> getBundlers();
+
+    /**
+     * Returns all of the preconfigured, requested, and manually
+     * configured bundlers loaded with this instance that are of
+     * a specific BundleType, such as disk images, installers, or
+     * remote installers.
+     *
+     * @return a read-only collection of the requested bundlers
+     */
+    Collection<Bundler> getBundlers(String type);
+
+    /**
+     * Loads the bundlers common to the JDK.  A typical implementation
+     * would load:
+     * <UL>
+     *     <LI>Windows file image</LI>
+     *     <LI>Mac .app</LI>
+     *     <LI>Linux file image</LI>
+
+     *     <LI>Windows MSI</LI>
+     *     <LI>Windows EXE</LI>
+     *     <LI>Mac DMG</LI>
+     *     <LI>Mac PKG</LI>
+     *     <LI>Linux DEB</LI>
+     *     <LI>Linux RPM</LI>
+     *
+     * </UL>
+     *
+     * This method is called from the
+     * {@link #createBundlersInstance(ClassLoader)}
+     * and {@link #createBundlersInstance()} methods.
+     * NOTE: Because of the module system this method is now not used.
+     */
+    void loadDefaultBundlers();
+
+    /**
+     * Loads bundlers from the META-INF/services directly.
+     *
+     * This method is called from the
+     * {@link #createBundlersInstance(ClassLoader)}
+     * and {@link #createBundlersInstance()} methods.
+     */
+    void loadBundlersFromServices(ClassLoader cl);
+
+    /**
+     * Loads a specific bundler into the set of bundlers.
+     * Useful for a manually configured bundler.
+     *
+     * @param bundler the specific bundler to add
+     */
+    void loadBundler(Bundler bundler);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/CLIHelp.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jpackage.internal;
+
+import java.util.ResourceBundle;
+import java.io.File;
+import java.text.MessageFormat;
+
+
+/**
+ * CLIHelp
+ *
+ * Generate and show the command line interface help message(s).
+ */
+public class CLIHelp {
+
+    private static final ResourceBundle I18N = ResourceBundle.getBundle(
+            "jdk.jpackage.internal.resources.HelpResources");
+
+    // generates --help for jpackage's CLI
+    public static void showHelp(boolean noArgs) {
+
+        Platform platform = Platform.getPlatform();
+        if (noArgs) {
+            Log.info(MessageFormat.format(
+                     I18N.getString("MSG_Help_no_args"), File.pathSeparator));
+        } else {
+            Log.info(MessageFormat.format(
+                    I18N.getString("MSG_Help_common"), File.pathSeparator));
+
+            switch (platform) {
+                case MAC:
+                    Log.info(I18N.getString("MSG_Help_mac"));
+                    if (Log.isDebug()) {
+                        Log.info(I18N.getString("MSG_Help_win"));
+                        Log.info(I18N.getString("MSG_Help_linux"));
+                    }
+                    break;
+                case LINUX:
+                    Log.info(I18N.getString("MSG_Help_linux"));
+                    if (Log.isDebug()) {
+                        Log.info(I18N.getString("MSG_Help_win"));
+                        Log.info(I18N.getString("MSG_Help_mac"));
+                    }
+                    break;
+                case WINDOWS:
+                    Log.info(I18N.getString("MSG_Help_win"));
+                    if (Log.isDebug()) {
+                        Log.info(I18N.getString("MSG_Help_mac"));
+                        Log.info(I18N.getString("MSG_Help_linux"));
+                    }
+                    break;
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ConfigException.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2012, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jpackage.internal;
+
+public class ConfigException extends Exception {
+    private static final long serialVersionUID = 1L;
+    final String advice;
+
+    public ConfigException(String msg, String advice) {
+        super(msg);
+        this.advice = advice;
+    }
+
+    public ConfigException(Exception cause) {
+        super(cause);
+        this.advice = null;
+    }
+
+    public String getAdvice() {
+        return advice;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/DeployParams.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,613 @@
+/*
+ * Copyright (c) 2011, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jpackage.internal;
+
+import java.io.File;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.InvalidPathException;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+/**
+ * DeployParams
+ *
+ * This class is generated and used in Arguments.processArguments() as
+ * intermediate step in generating the BundleParams and ultimately the Bundles
+ */
+public class DeployParams {
+
+    final List<RelativeFileSet> resources = new ArrayList<>();
+
+    String id;
+    String title;
+    String vendor;
+    String email;
+    String description;
+    String category;
+    String licenseType;
+    String copyright;
+    String version;
+    Boolean systemWide;
+    Boolean serviceHint;
+    Boolean signBundle;
+    Boolean installdirChooser;
+
+    String applicationClass;
+
+    List<Param> params;
+    List<String> arguments; //unnamed arguments
+
+    // Java 9 modules support
+    String addModules = null;
+    String limitModules = null;
+    Boolean stripNativeCommands = null;
+    String modulePath = null;
+    String module = null;
+    String debugPort = null;
+
+    File outdir = null;
+
+    String appId = null;
+
+    // list of jvm args
+    // (in theory string can contain spaces and need to be escaped
+    List<String> jvmargs = new LinkedList<>();
+
+    // raw arguments to the bundler
+    Map<String, ? super Object> bundlerArguments = new LinkedHashMap<>();
+
+    void setCategory(String category) {
+        this.category = category;
+    }
+
+    void setLicenseType(String licenseType) {
+        this.licenseType = licenseType;
+    }
+
+    void setCopyright(String copyright) {
+        this.copyright = copyright;
+    }
+
+    void setVersion(String version) {
+        this.version = version;
+    }
+
+    void setSystemWide(Boolean systemWide) {
+        this.systemWide = systemWide;
+    }
+
+    void setInstalldirChooser(Boolean installdirChooser) {
+        this.installdirChooser = installdirChooser;
+    }
+
+    void setSignBundle(Boolean signBundle) {
+        this.signBundle = signBundle;
+    }
+
+    void addJvmArg(String v) {
+        jvmargs.add(v);
+    }
+
+    void setArguments(List<String> args) {
+        this.arguments = args;
+    }
+
+    List<String> getArguments() {
+        return this.arguments;
+    }
+
+    void addArgument(String arg) {
+        this.arguments.add(arg);
+    }
+
+    void addAddModule(String value) {
+        if (addModules == null) {
+            addModules = value;
+        }
+        else {
+            addModules += "," + value;
+        }
+    }
+
+    void addLimitModule(String value) {
+        if (limitModules == null) {
+            limitModules = value;
+        }
+        else {
+            limitModules += "," + value;
+        }
+    }
+
+    String getModulePath() {
+        return this.modulePath;
+    }
+
+    void setModulePath(String value) {
+        this.modulePath = value;
+    }
+
+    void setModule(String value) {
+        this.module = value;
+    }
+
+    void setDebug(String value) {
+        this.debugPort = value;
+    }
+
+    void setStripNativeCommands(boolean value) {
+        this.stripNativeCommands = value;
+    }
+
+    void setDescription(String description) {
+        this.description = description;
+    }
+
+    public void setAppId(String id) {
+        appId = id;
+    }
+
+    void setParams(List<Param> params) {
+        this.params = params;
+    }
+
+    void setTitle(String title) {
+        this.title = title;
+    }
+
+    void setVendor(String vendor) {
+        this.vendor = vendor;
+    }
+
+    void setEmail(String email) {
+        this.email = email;
+    }
+
+    void setApplicationClass(String applicationClass) {
+        this.applicationClass = applicationClass;
+    }
+
+    File getOutput() {
+        return outdir;
+    }
+
+    public void setOutput(File output) {
+        outdir = output;
+    }
+
+    static class Template {
+        File in;
+        File out;
+
+        Template(File in, File out) {
+            this.in = in;
+            this.out = out;
+        }
+    }
+
+    // we need to expand as in some cases
+    // (most notably jpackage)
+    // we may get "." as filename and assumption is we include
+    // everything in the given folder
+    // (IOUtils.copyfiles() have recursive behavior)
+    List<File> expandFileset(File root) {
+        List<File> files = new LinkedList<>();
+        if (!Files.isSymbolicLink(root.toPath())) {
+            if (root.isDirectory()) {
+                File[] children = root.listFiles();
+                if (children != null) {
+                    for (File f : children) {
+                        files.addAll(expandFileset(f));
+                    }
+                }
+            } else {
+                files.add(root);
+            }
+        }
+        return files;
+    }
+
+    public void addResource(File baseDir, String path) {
+        File file = new File(baseDir, path);
+        // normalize top level dir
+        // to strip things like "." in the path
+        // or it can confuse symlink detection logic
+        file = file.getAbsoluteFile();
+
+        if (baseDir == null) {
+            baseDir = file.getParentFile();
+        }
+        resources.add(new RelativeFileSet(
+                baseDir, new LinkedHashSet<>(expandFileset(file))));
+    }
+
+    public void addResource(File baseDir, File file) {
+        // normalize initial file
+        // to strip things like "." in the path
+        // or it can confuse symlink detection logic
+        file = file.getAbsoluteFile();
+
+        if (baseDir == null) {
+            baseDir = file.getParentFile();
+        }
+        resources.add(new RelativeFileSet(
+                baseDir, new LinkedHashSet<>(expandFileset(file))));
+    }
+
+    void setClasspath() {
+        String classpath = "";
+        for (RelativeFileSet resource : resources) {
+             for (String file : resource.getIncludedFiles()) {
+                 if (file.endsWith(".jar")) {
+                     classpath += file + File.pathSeparator;
+                 }
+             }
+        }
+        addBundleArgument(
+                StandardBundlerParam.CLASSPATH.getID(), classpath);
+    }
+
+    private static File createFile(final File baseDir, final String path) {
+        final File testFile = new File(path);
+        return testFile.isAbsolute() ?
+                testFile : new File(baseDir == null ?
+                        null : baseDir.getAbsolutePath(), path);
+    }
+
+    static void validateName(String s, boolean forApp)
+            throws PackagerException {
+        
+        String exceptionKey = forApp ?
+            "ERR_InvalidAppName" : "ERR_InvalidSLName";
+        
+        if (s == null) {
+            if (forApp) {
+                return;
+            } else {
+                throw new PackagerException(exceptionKey, s);
+            }
+        }
+        if (s.length() == 0 || s.charAt(s.length() - 1) == '\\') {
+            throw new PackagerException(exceptionKey, s);
+        }
+        try {
+            // name must be valid path element for this file system
+            Path p = (new File(s)).toPath();
+            // and it must be a single name element in a path
+            if (p.getNameCount() != 1) {
+                throw new PackagerException(exceptionKey, s);
+            }
+        } catch (InvalidPathException ipe) {
+            throw new PackagerException(ipe, exceptionKey, s);
+        }
+
+        for (int i = 0; i < s.length(); i++) {
+            char a = s.charAt(i);
+            // We check for ASCII codes first which we accept. If check fails,
+            // check if it is acceptable extended ASCII or unicode character.
+            if (a < ' ' || a > '~') {
+                // Accept anything else including special chars like copyright
+                // symbols. Note: space will be included by ASCII check above,
+                // but other whitespace like tabs or new line will be rejected.
+                if (Character.isISOControl(a)  ||
+                        Character.isWhitespace(a)) {
+                    throw new PackagerException(exceptionKey, s);
+                }
+            } else if (a == '"' || a == '%') {
+                throw new PackagerException(exceptionKey, s);
+            }
+        }
+    }
+
+    public void validate() throws PackagerException {
+        if (outdir == null) {
+            throw new PackagerException("ERR_MissingArgument", "--output");
+        }
+
+        boolean hasModule = (bundlerArguments.get(
+                Arguments.CLIOptions.MODULE.getId()) != null);
+        boolean hasImage = (bundlerArguments.get(
+                Arguments.CLIOptions.PREDEFINED_APP_IMAGE.getId()) != null);
+        boolean hasClass = (bundlerArguments.get(
+                Arguments.CLIOptions.APPCLASS.getId()) != null);
+        boolean hasMain = (bundlerArguments.get(
+                Arguments.CLIOptions.MAIN_JAR.getId()) != null);
+        boolean hasRuntimeImage = (bundlerArguments.get(
+                Arguments.CLIOptions.PREDEFINED_RUNTIME_IMAGE.getId()) != null);
+        boolean hasInput = (bundlerArguments.get(
+                Arguments.CLIOptions.INPUT.getId()) != null);
+        boolean hasModulePath = (bundlerArguments.get(
+                Arguments.CLIOptions.MODULE_PATH.getId()) != null);
+        boolean hasAppImage = (bundlerArguments.get(
+                Arguments.CLIOptions.PREDEFINED_APP_IMAGE.getId()) != null);
+        boolean runtimeInstaller = (bundlerArguments.get(
+                Arguments.CLIOptions.RUNTIME_INSTALLER.getId()) != null);
+
+        if (getBundleType() == BundlerType.IMAGE) {
+            // Module application requires --runtime-image or --module-path
+            if (hasModule) {
+                if (!hasModulePath && !hasRuntimeImage) {
+                    throw new PackagerException("ERR_MissingArgument",
+                            "--runtime-image or --module-path");
+                }
+            } else {
+                if (!hasInput) {
+                    throw new PackagerException(
+                           "ERR_MissingArgument", "--input");
+                }
+            }
+        } else if (getBundleType() == BundlerType.INSTALLER) {
+            if (!runtimeInstaller) {
+                if (hasModule) {
+                    if (!hasModulePath && !hasRuntimeImage && !hasAppImage) {
+                        throw new PackagerException("ERR_MissingArgument",
+                            "--runtime-image, --module-path or --app-image");
+                    }
+                } else {
+                    if (!hasInput && !hasAppImage) {
+                        throw new PackagerException("ERR_MissingArgument",
+                                "--input or --app-image");
+                    }
+                }
+            }
+        }
+
+        // if bundling non-modular image, or installer without app-image
+        // then we need some resources and a main class
+        if (!hasModule && !hasImage && !runtimeInstaller) {
+            if (resources.isEmpty()) {
+                throw new PackagerException("ERR_MissingAppResources");
+            }
+            if (!hasClass) {
+                throw new PackagerException("ERR_MissingArgument",
+                        "--main-class");
+            }
+            if (!hasMain) {
+                throw new PackagerException("ERR_MissingArgument",
+                        "--main-jar");
+            }
+        }
+
+        String name = (String)bundlerArguments.get(
+                Arguments.CLIOptions.NAME.getId());
+        validateName(name, true);
+
+        // Validate app image if set
+        String appImage = (String)bundlerArguments.get(
+                Arguments.CLIOptions.PREDEFINED_APP_IMAGE.getId());
+        if (appImage != null) {
+            File appImageDir = new File(appImage);
+            if (!appImageDir.exists() || appImageDir.list().length == 0) {
+                throw new PackagerException("ERR_AppImageNotExist", appImage);
+            }
+        }
+
+        // Validate build-root
+        String root = (String)bundlerArguments.get(
+                Arguments.CLIOptions.BUILD_ROOT.getId());
+        if (root != null) {
+            String [] contents = (new File(root)).list();
+
+            if (contents != null && contents.length > 0) {
+                throw new PackagerException("ERR_BuildRootInvalid", root);
+            }
+        }
+
+        // Validate license file if set
+        String license = (String)bundlerArguments.get(
+                Arguments.CLIOptions.LICENSE_FILE.getId());
+        if (license != null) {
+            File licenseFile = new File(license);
+            if (!licenseFile.exists()) {
+                throw new PackagerException("ERR_LicenseFileNotExit");
+            }
+        }
+    }
+
+    boolean validateForBundle() {
+        boolean result = false;
+
+        // Success
+        if (((applicationClass != null && !applicationClass.isEmpty()) ||
+            (module != null && !module.isEmpty()))) {
+            result = true;
+        }
+
+        return result;
+    }
+
+    BundlerType bundleType = BundlerType.NONE;
+    String targetFormat = null; //means any
+
+    void setBundleType(BundlerType type) {
+        bundleType = type;
+    }
+
+    BundlerType getBundleType() {
+        return bundleType;
+    }
+
+    void setTargetFormat(String t) {
+        targetFormat = t;
+    }
+
+    String getTargetFormat() {
+        return targetFormat;
+    }
+
+    private String getArch() {
+        String arch = System.getProperty("os.arch").toLowerCase();
+
+        if ("x86".equals(arch) || "i386".equals(arch) || "i486".equals(arch)
+                || "i586".equals(arch) || "i686".equals(arch)) {
+            arch = "x86";
+        } else if ("x86_64".equals(arch) || "amd64".equals("arch")) {
+            arch = "x86_64";
+        }
+
+        return arch;
+    }
+
+    static final Set<String> multi_args = new TreeSet<>(Arrays.asList(
+            StandardBundlerParam.JVM_OPTIONS.getID(),
+            StandardBundlerParam.ARGUMENTS.getID(),
+            StandardBundlerParam.MODULE_PATH.getID(),
+            StandardBundlerParam.ADD_MODULES.getID(),
+            StandardBundlerParam.LIMIT_MODULES.getID(),
+            StandardBundlerParam.FILE_ASSOCIATIONS.getID()
+    ));
+
+    @SuppressWarnings("unchecked")
+    public void addBundleArgument(String key, Object value) {
+        // special hack for multi-line arguments
+        if (multi_args.contains(key)) {
+            Object existingValue = bundlerArguments.get(key);
+            if (existingValue instanceof String && value instanceof String) {
+                bundlerArguments.put(key, existingValue + "\n\n" + value);
+            } else if (existingValue instanceof List && value instanceof List) {
+                ((List)existingValue).addAll((List)value);
+            } else if (existingValue instanceof Map &&
+                value instanceof String && ((String)value).contains("=")) {
+                String[] mapValues = ((String)value).split("=", 2);
+                ((Map)existingValue).put(mapValues[0], mapValues[1]);
+            } else {
+                bundlerArguments.put(key, value);
+            }
+        } else {
+            bundlerArguments.put(key, value);
+        }
+    }
+
+    BundleParams getBundleParams() {
+        BundleParams bundleParams = new BundleParams();
+
+        // construct app resources relative to output folder!
+        bundleParams.setAppResourcesList(resources);
+
+        bundleParams.setIdentifier(id);
+
+        bundleParams.setApplicationClass(applicationClass);
+        bundleParams.setAppVersion(version);
+        bundleParams.setType(bundleType);
+        bundleParams.setBundleFormat(targetFormat);
+        bundleParams.setVendor(vendor);
+        bundleParams.setEmail(email);
+        bundleParams.setInstalldirChooser(installdirChooser);
+        bundleParams.setCopyright(copyright);
+        bundleParams.setApplicationCategory(category);
+        bundleParams.setDescription(description);
+        bundleParams.setTitle(title);
+
+        bundleParams.setJvmargs(jvmargs);
+        bundleParams.setArguments(arguments);
+
+        if (addModules != null && !addModules.isEmpty()) {
+            bundleParams.setAddModules(addModules);
+        }
+
+        if (limitModules != null && !limitModules.isEmpty()) {
+            bundleParams.setLimitModules(limitModules);
+        }
+
+        if (stripNativeCommands != null) {
+            bundleParams.setStripNativeCommands(stripNativeCommands);
+        }
+
+        if (modulePath != null && !modulePath.isEmpty()) {
+            bundleParams.setModulePath(modulePath);
+        }
+
+        if (module != null && !module.isEmpty()) {
+            bundleParams.setMainModule(module);
+        }
+
+        if (debugPort != null && !debugPort.isEmpty()) {
+            bundleParams.setDebug(debugPort);
+        }
+
+        Map<String, String> paramsMap = new TreeMap<>();
+        if (params != null) {
+            for (Param p : params) {
+                paramsMap.put(p.name, p.value);
+            }
+        }
+
+        Map<String, String> unescapedHtmlParams = new TreeMap<>();
+        Map<String, String> escapedHtmlParams = new TreeMap<>();
+
+        // check for collisions
+        TreeSet<String> keys = new TreeSet<>(bundlerArguments.keySet());
+        keys.retainAll(bundleParams.getBundleParamsAsMap().keySet());
+
+        if (!keys.isEmpty()) {
+            throw new RuntimeException("Deploy Params and Bundler Arguments "
+                    + "overlap in the following values:" + keys.toString());
+        }
+
+        bundleParams.addAllBundleParams(bundlerArguments);
+
+        return bundleParams;
+    }
+
+    Map<String, ? super Object> getBundlerArguments() {
+        return this.bundlerArguments;
+    }
+
+    void putUnlessNull(String param, Object value) {
+        if (value != null) {
+            bundlerArguments.put(param, value);
+        }
+    }
+
+    void putUnlessNullOrEmpty(String param, Map<?, ?> value) {
+        if (value != null && !value.isEmpty()) {
+            bundlerArguments.put(param, value);
+        }
+    }
+
+    void putUnlessNullOrEmpty(String param, Collection<?> value) {
+        if (value != null && !value.isEmpty()) {
+            bundlerArguments.put(param, value);
+        }
+    }
+
+    @Override
+    public String toString() {
+        return "DeployParams {" + "output: " + outdir
+                + " resources: {" + resources + "}}";
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/EnumeratedBundlerParam.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2014, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jpackage.internal;
+
+import java.util.*;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+
+/**
+ * EnumeratedBundlerParams<T>
+ *
+ * Contains key-value pairs (elements) where keys are "displayable"
+ * keys which the IDE can display/choose and values are "identifier" values
+ * which can be stored in parameters' map.
+ *
+ * For instance the Mac has a predefined set of categories which can be applied
+ * to LSApplicationCategoryType which is required for the mac app store.
+ *
+ * The following example illustrates a simple usage of
+ *     the MAC_CATEGORY parameter:
+ *
+ * <pre>{@code
+ *     Set<String> keys = MAC_CATEGORY.getDisplayableKeys();
+ *
+ *     String key = getLastValue(keys); // get last value for example
+ *
+ *     String value = MAC_CATEGORY.getValueForDisplayableKey(key);
+ *     params.put(MAC_CATEGORY.getID(), value);
+ * }</pre>
+ *
+ */
+class EnumeratedBundlerParam<T> extends BundlerParamInfo<T> {
+    // Not sure if this is the correct order, my idea is that from IDE
+    // perspective the string to display to the user is the key and then the
+    // value is some type of object (although probably a String in most cases)
+    private final Map<String, T> elements;
+    private final boolean strict;
+
+    EnumeratedBundlerParam(String name, String description,
+            String id, Class<T> valueType,
+            Function<Map<String, ? super Object>, T> defaultValueFunction,
+            BiFunction<String, Map<String, ? super Object>, T> stringConverter,
+            Map<String, T> elements, boolean strict) {
+        this.name = name;
+        this.description = description;
+        this.id = id;
+        this.valueType = valueType;
+        this.defaultValueFunction = defaultValueFunction;
+        this.stringConverter = stringConverter;
+        this.elements = elements;
+        this.strict = strict;
+    }
+
+    boolean isInPossibleValues(T value) {
+        return elements.values().contains(value);
+    }
+
+    // Having the displayable values as the keys seems a bit wacky
+    Set<String> getDisplayableKeys() {
+        return Collections.unmodifiableSet(elements.keySet());
+    }
+
+    // mapping from a "displayable" key to an "identifier" value.
+    T getValueForDisplayableKey(String displayableKey) {
+        return elements.get(displayableKey);
+    }
+
+    boolean isStrict() {
+        return strict;
+    }
+
+    boolean isLoose() {
+        return !isStrict();
+    }
+
+    T validatedFetchFrom(Map<String, ? super Object> params)
+            throws InvalidBundlerParamException {
+        if (isStrict()) {
+            T value = fetchFrom(params);
+            if (!isInPossibleValues(value)) {
+                throw new InvalidBundlerParamException("Parameter "
+                        + value.toString()
+                        + " not in valid set of values for BundlerParam "
+                        + name);
+            }
+            return value;
+        }
+        return fetchFrom(params);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/IOUtils.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,312 @@
+/*
+ * Copyright (c) 2012, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jpackage.internal;
+
+import java.io.*;
+import java.net.URL;
+import java.util.Arrays;
+import java.nio.channels.FileChannel;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * IOUtils
+ *
+ * A collection of static utility methods.
+ */
+public class IOUtils {
+
+    public static void deleteRecursive(File path) throws IOException {
+        if (!path.exists()) {
+            return;
+        }
+        Path directory = path.toPath();
+        Files.walkFileTree(directory, new SimpleFileVisitor<Path>() {
+            @Override
+            public FileVisitResult visitFile(Path file,
+                            BasicFileAttributes attr) throws IOException {
+                if (Platform.getPlatform() == Platform.WINDOWS) {
+                    Files.setAttribute(file, "dos:readonly", false);
+                }
+                Files.delete(file);
+                return FileVisitResult.CONTINUE;
+            }
+
+            @Override
+            public FileVisitResult preVisitDirectory(Path dir,
+                            BasicFileAttributes attr) throws IOException {
+                if (Platform.getPlatform() == Platform.WINDOWS) {
+                    Files.setAttribute(dir, "dos:readonly", false);
+                }
+                return FileVisitResult.CONTINUE;
+            }
+
+            @Override
+            public FileVisitResult postVisitDirectory(Path dir, IOException e)
+                            throws IOException {
+                Files.delete(dir);
+                return FileVisitResult.CONTINUE;
+            }
+        });
+    }
+
+    public static void copyRecursive(Path src, Path dest) throws IOException {
+        Files.walkFileTree(src, new SimpleFileVisitor<Path>() {
+            @Override
+            public FileVisitResult preVisitDirectory(final Path dir,
+                    final BasicFileAttributes attrs) throws IOException {
+                Files.createDirectories(dest.resolve(src.relativize(dir)));
+                return FileVisitResult.CONTINUE;
+            }
+
+            @Override
+            public FileVisitResult visitFile(final Path file,
+                    final BasicFileAttributes attrs) throws IOException {
+                Files.copy(file, dest.resolve(src.relativize(file)));
+                return FileVisitResult.CONTINUE;
+            }
+        });
+    }
+
+    public static void copyRecursive(Path src, Path dest,
+            final List<String> excludes) throws IOException {
+        Files.walkFileTree(src, new SimpleFileVisitor<Path>() {
+            @Override
+            public FileVisitResult preVisitDirectory(final Path dir,
+                    final BasicFileAttributes attrs) throws IOException {
+                if (excludes.contains(dir.toFile().getName())) {
+                    return FileVisitResult.SKIP_SUBTREE;
+                } else {
+                    Files.createDirectories(dest.resolve(src.relativize(dir)));
+                    return FileVisitResult.CONTINUE;
+                }
+            }
+
+            @Override
+            public FileVisitResult visitFile(final Path file,
+                    final BasicFileAttributes attrs) throws IOException {
+                if (!excludes.contains(file.toFile().getName())) {
+                    Files.copy(file, dest.resolve(src.relativize(file)));
+                }
+                return FileVisitResult.CONTINUE;
+            }
+        });
+    }
+
+    public static void copyFromURL(URL location, File file) throws IOException {
+        copyFromURL(location, file, false);
+    }
+
+    public static void copyFromURL(URL location, File file, boolean append)
+            throws IOException {
+        if (location == null) {
+            throw new IOException("Missing input resource!");
+        }
+        if (file.exists() && !append) {
+           file.delete();
+        }
+        InputStream in = location.openStream();
+        FileOutputStream out = new FileOutputStream(file, append);
+        byte[] buffer = new byte[1024];
+        int len;
+        while ((len = in.read(buffer)) != -1) {
+            out.write(buffer, 0, len);
+        }
+        out.close();
+        in.close();
+        file.setReadOnly();
+        file.setReadable(true, false);
+    }
+
+    public static void copyFile(File sourceFile, File destFile)
+            throws IOException {
+        destFile.getParentFile().mkdirs();
+
+        //recreate the file as existing copy may have weird permissions
+        destFile.delete();
+        destFile.createNewFile();
+
+        FileChannel source = null;
+        FileChannel destination = null;
+        source = new FileInputStream(sourceFile).getChannel();
+        destination = new FileOutputStream(destFile).getChannel();
+        if (destination != null && source != null) {
+            destination.transferFrom(source, 0, source.size());
+        }
+        if (source != null) {
+            source.close();
+        }
+        if (destination != null) {
+            destination.close();
+        }
+
+        //preserve executable bit!
+        if (sourceFile.canExecute()) {
+            destFile.setExecutable(true, false);
+        }
+        if (!sourceFile.canWrite()) {
+            destFile.setReadOnly();
+        }
+        destFile.setReadable(true, false);
+    }
+
+    public static long getFolderSize(File folder) {
+        long foldersize = 0;
+
+        File[] children = folder.listFiles();
+        if (children != null) {
+            for (File f : children) {
+                if (f.isDirectory()) {
+                    foldersize += getFolderSize(f);
+                } else {
+                    foldersize += f.length();
+                }
+            }
+        }
+
+        return foldersize;
+    }
+
+    // run "launcher paramfile" in the directory where paramfile is kept
+    public static void run(String launcher, File paramFile, boolean verbose)
+            throws IOException {
+        if (paramFile != null && paramFile.exists()) {
+            ProcessBuilder pb =
+                    new ProcessBuilder(launcher, paramFile.getName());
+            pb = pb.directory(paramFile.getParentFile());
+            exec(pb, verbose);
+        }
+    }
+
+    public static void exec(ProcessBuilder pb, boolean verbose)
+            throws IOException {
+        exec(pb, verbose, false);
+    }
+
+    public static void exec(ProcessBuilder pb, boolean verbose,
+            boolean testForPresenseOnly) throws IOException {
+        exec(pb, verbose, testForPresenseOnly, null);
+    }
+
+    public static void exec(ProcessBuilder pb, boolean verbose,
+            boolean testForPresenseOnly, PrintStream consumer)
+            throws IOException {
+        pb.redirectErrorStream(true);
+        Log.verbose("Running "
+                + Arrays.toString(pb.command().toArray(new String[0]))
+                + (pb.directory() != null ? (" in " + pb.directory()) : ""));
+        Process p = pb.start();
+        InputStreamReader isr = new InputStreamReader(p.getInputStream());
+        BufferedReader br = new BufferedReader(isr);
+        String lineRead;
+        while ((lineRead = br.readLine()) != null) {
+            if (consumer != null) {
+                consumer.print(lineRead + '\n');
+            } else {
+               Log.verbose(lineRead);
+            }
+        }
+        try {
+            int ret = p.waitFor();
+            if (ret != 0 && !(testForPresenseOnly && ret != 127)) {
+                throw new IOException("Exec failed with code "
+                        + ret + " command ["
+                        + Arrays.toString(pb.command().toArray(new String[0]))
+                        + " in " + (pb.directory() != null ?
+                                pb.directory().getAbsolutePath() :
+                                "unspecified directory"));
+            }
+        } catch (InterruptedException ex) {
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    private static Process startProcess(Object... args) throws IOException {
+        final ArrayList<String> argsList = new ArrayList<>();
+        for (Object a : args) {
+            if (a instanceof List) {
+                argsList.addAll((List)a);
+            } else if (a instanceof String) {
+                argsList.add((String)a);
+            }
+        }
+
+        return Runtime.getRuntime().exec(
+                argsList.toArray(new String[argsList.size()]));
+    }
+
+    private static void logErrorStream(Process p) {
+        final BufferedReader err =
+                new BufferedReader(new InputStreamReader(p.getErrorStream()));
+        Thread t = new Thread(() -> {
+            try {
+                String line;
+                while ((line = err.readLine()) != null) {
+                    Log.error(line);
+                }
+            } catch (IOException ioe) {
+                Log.verbose(ioe);
+            }
+        });
+        t.setDaemon(true);
+        t.start();
+    }
+
+    public static int getProcessOutput(List<String> result, Object... args)
+            throws IOException, InterruptedException {
+        final Process p = startProcess(args);
+
+        List<String> list = new ArrayList<>();
+        final BufferedReader in =
+                new BufferedReader(new InputStreamReader(p.getInputStream()));
+        Thread t = new Thread(() -> {
+            try {
+                String line;
+                while ((line = in.readLine()) != null) {
+                    list.add(line);
+                }
+            } catch (IOException ioe) {
+                jdk.jpackage.internal.Log.verbose(ioe);
+            }
+        });
+        t.setDaemon(true);
+        t.start();
+
+        logErrorStream(p);
+
+        int ret = p.waitFor();
+
+        result.clear();
+        result.addAll(list);
+
+        return ret;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/InvalidBundlerParamException.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2014, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jpackage.internal;
+
+public class InvalidBundlerParamException extends RuntimeException {
+    private static final long serialVersionUID = 1L;
+    public InvalidBundlerParamException(String message) {
+        super(message);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/JLinkBundlerHelper.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,485 @@
+/*
+ * Copyright (c) 2015, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jpackage.internal;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.StringReader;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.ResourceBundle;
+import java.util.Set;
+import java.util.Optional;
+import java.util.Arrays;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import java.util.regex.Matcher;
+import java.util.spi.ToolProvider;
+import java.lang.module.Configuration;
+import java.lang.module.ResolvedModule;
+import java.lang.module.ModuleDescriptor;
+import java.lang.module.ModuleFinder;
+import java.lang.module.ModuleReference;
+
+final class JLinkBundlerHelper {
+
+    private static final ResourceBundle I18N = ResourceBundle.getBundle(
+            "jdk.jpackage.internal.resources.MainResources");
+    private static final String JRE_MODULES_FILENAME =
+            "jdk/jpackage/internal/resources/jre.list";
+    private static final String SERVER_JRE_MODULES_FILENAME =
+            "jdk/jpackage/internal/resources/jre.module.list";
+
+    static final ToolProvider JLINK_TOOL =
+            ToolProvider.findFirst("jlink").orElseThrow();
+
+    private JLinkBundlerHelper() {}
+
+    @SuppressWarnings("unchecked")
+    static final BundlerParamInfo<Integer> DEBUG =
+            new StandardBundlerParam<>(
+                    "",
+                    "",
+                    "-J-Xdebug",
+                    Integer.class,
+                    p -> null,
+                    (s, p) -> {
+                        return Integer.valueOf(s);
+                    });
+
+    static String listOfPathToString(List<Path> value) {
+        String result = "";
+
+        for (Path path : value) {
+            if (result.length() > 0) {
+                result += File.pathSeparator;
+            }
+
+            result += path.toString();
+        }
+
+        return result;
+    }
+
+    static String setOfStringToString(Set<String> value) {
+        String result = "";
+
+        for (String element : value) {
+            if (result.length() > 0) {
+                result += ",";
+            }
+
+            result += element;
+        }
+
+        return result;
+    }
+
+    static File getMainJar(Map<String, ? super Object> params) {
+        File result = null;
+        RelativeFileSet fileset =
+                StandardBundlerParam.MAIN_JAR.fetchFrom(params);
+
+        if (fileset != null) {
+            String filename = fileset.getIncludedFiles().iterator().next();
+            result = fileset.getBaseDirectory().toPath().
+                    resolve(filename).toFile();
+
+            if (result == null || !result.exists()) {
+                String srcdir =
+                    StandardBundlerParam.SOURCE_DIR.fetchFrom(params);
+
+                if (srcdir != null) {
+                    result = new File(srcdir + File.separator + filename);
+                }
+            }
+        }
+
+        return result;
+    }
+
+    static String getMainClass(Map<String, ? super Object> params) {
+        String result = "";
+        String mainModule = StandardBundlerParam.MODULE.fetchFrom(params);
+        if (mainModule != null)  {
+            int index = mainModule.indexOf("/");
+            if (index > 0) {
+                result = mainModule.substring(index + 1);
+            }
+        } else {
+            RelativeFileSet fileset =
+                    StandardBundlerParam.MAIN_JAR.fetchFrom(params);
+            if (fileset != null) {
+                result = StandardBundlerParam.MAIN_CLASS.fetchFrom(params);
+            } else {
+                // possibly app-image
+            }
+        }
+
+        return result;
+    }
+
+    static String getMainModule(Map<String, ? super Object> params) {
+        String result = null;
+        String mainModule = StandardBundlerParam.MODULE.fetchFrom(params);
+
+        if (mainModule != null) {
+            int index = mainModule.indexOf("/");
+
+            if (index > 0) {
+                result = mainModule.substring(0, index);
+            } else {
+                result = mainModule;
+            }
+        }
+
+        return result;
+    }
+
+    private static Set<String> getValidModules(List<Path> modulePath,
+            Set<String> addModules, Set<String> limitModules) {
+        ModuleHelper moduleHelper = new ModuleHelper(
+                modulePath, addModules, limitModules);
+        return removeInvalidModules(modulePath, moduleHelper.modules());
+    }
+
+    static void execute(Map<String, ? super Object> params,
+            AbstractAppImageBuilder imageBuilder)
+            throws IOException, Exception {
+        List<Path> modulePath =
+                StandardBundlerParam.MODULE_PATH.fetchFrom(params);
+        Set<String> addModules =
+                StandardBundlerParam.ADD_MODULES.fetchFrom(params);
+        Set<String> limitModules =
+                StandardBundlerParam.LIMIT_MODULES.fetchFrom(params);
+        boolean stripNativeCommands =
+                StandardBundlerParam.STRIP_NATIVE_COMMANDS.fetchFrom(params);
+        Path outputDir = imageBuilder.getRoot();
+        String excludeFileList = imageBuilder.getExcludeFileList();
+        File mainJar = getMainJar(params);
+        ModFile.ModType mainJarType = ModFile.ModType.Unknown;
+
+        if (mainJar != null) {
+            mainJarType = new ModFile(mainJar).getModType();
+        } else if (StandardBundlerParam.MODULE.fetchFrom(params) == null) {
+            // user specified only main class, all jars will be on the classpath
+            mainJarType = ModFile.ModType.UnnamedJar;
+        }
+
+        // Modules
+        String mainModule = getMainModule(params);
+        if (mainJarType == ModFile.ModType.UnnamedJar) {
+            // The default for an unnamed jar is ALL_DEFAULT
+            addModules.add(ModuleHelper.ALL_DEFAULT);
+        } else if (mainJarType == ModFile.ModType.Unknown ||
+                mainJarType == ModFile.ModType.ModularJar) {
+            if (mainModule == null) {
+                addModules.add(ModuleHelper.ALL_DEFAULT);
+            }
+        } 
+
+        Set<String> validModules =
+                  getValidModules(modulePath, addModules, limitModules);
+        if (mainModule != null) {
+            validModules.add(mainModule);
+        }
+
+        Log.verbose(MessageFormat.format(
+                I18N.getString("message.modules"), validModules.toString()));
+
+        runJLink(outputDir, modulePath, validModules, limitModules,
+                excludeFileList, stripNativeCommands,
+                new HashMap<String,String>());
+
+        imageBuilder.prepareApplicationFiles();
+    }
+
+
+    static void generateJre(Map<String, ? super Object> params,
+            AbstractAppImageBuilder imageBuilder)
+            throws IOException, Exception {
+        List<Path> modulePath =
+                StandardBundlerParam.MODULE_PATH.fetchFrom(params);
+        Set<String> addModules =
+                StandardBundlerParam.ADD_MODULES.fetchFrom(params);
+        Set<String> limitModules =
+                StandardBundlerParam.LIMIT_MODULES.fetchFrom(params);
+        boolean stripNativeCommands =
+                StandardBundlerParam.STRIP_NATIVE_COMMANDS.fetchFrom(params);
+        Path outputDir = imageBuilder.getRoot();
+        addModules.add(ModuleHelper.ALL_MODULE_PATH);
+        Set<String> redistModules = getValidModules(modulePath,
+                addModules, limitModules);
+        addModules.addAll(redistModules);
+
+        Log.verbose(MessageFormat.format(
+                I18N.getString("message.modules"), addModules.toString()));
+
+        runJLink(outputDir, modulePath, addModules, limitModules,
+                null, stripNativeCommands, new HashMap<String,String>());
+
+        imageBuilder.prepareJreFiles();
+    }
+
+    // Returns the path to the JDK modules in the user defined module path.
+    static Path findPathOfModule( List<Path> modulePath, String moduleName) {
+
+        for (Path path : modulePath) {
+            Path moduleNamePath = path.resolve(moduleName);
+
+            if (Files.exists(moduleNamePath)) {
+                return path;
+            }
+        }
+
+        return null;
+    }
+
+    /*
+     * Returns the set of modules that would be visible by default for
+     * a non-modular-aware application consisting of the given elements.
+     */
+    private static Set<String> getDefaultModules(
+            Path[] paths, String[] addModules) {
+
+        // the modules in the run-time image that export an API
+        Stream<String> systemRoots = ModuleFinder.ofSystem().findAll().stream()
+                .map(ModuleReference::descriptor)
+                .filter(descriptor -> exportsAPI(descriptor))
+                .map(ModuleDescriptor::name);
+
+        Set<String> roots;
+        if (addModules == null || addModules.length == 0) {
+            roots = systemRoots.collect(Collectors.toSet());
+        } else {
+            var extraRoots =  Stream.of(addModules);
+            roots = Stream.concat(systemRoots,
+                    extraRoots).collect(Collectors.toSet());
+        }
+
+        ModuleFinder finder = ModuleFinder.ofSystem();
+        if (paths != null && paths.length > 0) {
+            finder = ModuleFinder.compose(finder, ModuleFinder.of(paths));
+        }
+        return Configuration.empty()
+                .resolveAndBind(finder, ModuleFinder.of(), roots)
+                .modules()
+                .stream()
+                .map(ResolvedModule::name)
+                .collect(Collectors.toSet());
+    } 
+
+    /*
+     * Returns true if the given module exports an API to all module.
+     */
+    private static boolean exportsAPI(ModuleDescriptor descriptor) {
+        return descriptor.exports()
+                .stream()
+                .filter(e -> !e.isQualified())
+                .findAny()
+                .isPresent();
+    }
+
+    private static Set<String> removeInvalidModules(
+            List<Path> modulePath, Set<String> modules) {
+        Set<String> result = new LinkedHashSet<String>();
+        ModuleManager mm = new ModuleManager(modulePath);
+        List<ModFile> lmodfiles =
+                mm.getModules(EnumSet.of(ModuleManager.SearchType.ModularJar,
+                        ModuleManager.SearchType.Jmod,
+                        ModuleManager.SearchType.ExplodedModule));
+
+        HashMap<String, ModFile> validModules = new HashMap<>();
+
+        for (ModFile modFile : lmodfiles) {
+            validModules.put(modFile.getModName(), modFile);
+        }
+
+        for (String name : modules) {
+            if (validModules.containsKey(name)) {
+                result.add(name);
+            } else {
+                Log.error(MessageFormat.format(
+                        I18N.getString("warning.module.does.not.exist"), name));
+            }
+        }
+
+        return result;
+    }
+
+    private static class ModuleHelper {
+        // The token for "all modules on the module path".
+        private static final String ALL_MODULE_PATH = "ALL-MODULE-PATH";
+
+        // The token for "all valid runtime modules".
+        static final String ALL_DEFAULT = "ALL-DEFAULT";
+
+        private final Set<String> modules = new HashSet<>();
+        private enum Macros {None, AllModulePath, AllRuntime}
+
+        ModuleHelper(List<Path> paths, Set<String> addModules,
+                Set<String> limitModules) {
+            boolean addAllModulePath = false;
+            boolean addDefaultMods = false;
+            
+            for (Iterator<String> iterator = addModules.iterator();
+                    iterator.hasNext();) {
+                String module = iterator.next();
+
+                switch (module) {
+                    case ALL_MODULE_PATH:
+                        iterator.remove();
+                        addAllModulePath = true;
+                        break;
+                    case ALL_DEFAULT:
+                        iterator.remove();
+                        addDefaultMods = true;
+                        break;
+                    default:
+                        this.modules.add(module);
+                }
+            }
+
+            if (addAllModulePath) {
+                this.modules.addAll(getModuleNamesFromPath(paths));
+            } else if (addDefaultMods) {
+                this.modules.addAll(getDefaultModules(
+                        paths.toArray(new Path[0]),
+                        addModules.toArray(new String[0])));
+            }
+        }
+
+        Set<String> modules() {
+            return modules;
+        }
+
+        private static Set<String> getModuleNamesFromPath(List<Path> Value) {
+            Set<String> result = new LinkedHashSet<String>();
+            ModuleManager mm = new ModuleManager(Value);
+            List<ModFile> modFiles = mm.getModules(
+                    EnumSet.of(ModuleManager.SearchType.ModularJar,
+                    ModuleManager.SearchType.Jmod,
+                    ModuleManager.SearchType.ExplodedModule));
+
+            for (ModFile modFile : modFiles) {
+                result.add(modFile.getModName());
+            }
+            return result;
+        }
+    }
+
+    private static void runJLink(Path output, List<Path> modulePath,
+            Set<String> modules, Set<String> limitModules, String excludes,
+            boolean strip, HashMap<String, String> user) throws IOException {
+
+        // This is just to ensure jlink is given a non-existant directory
+        // The passed in output path should be non-existant or empty directory
+        IOUtils.deleteRecursive(output.toFile());
+
+        ArrayList<String> args = new ArrayList<String>();
+        args.add("--output");
+        args.add(output.toString());
+        if (modulePath != null && !modulePath.isEmpty()) {
+            args.add("--module-path");
+            args.add(getPathList(modulePath));
+        }
+        if (modules != null && !modules.isEmpty()) {
+            args.add("--add-modules");
+            args.add(getStringList(modules));
+        }
+        if (limitModules != null && !limitModules.isEmpty()) {
+            args.add("--limit-modules");
+            args.add(getStringList(limitModules));
+        }
+        if (excludes != null) {
+            args.add("--exclude-files");
+            args.add(excludes);
+        }
+        if (strip) {
+            args.add("--strip-native-commands");
+        }
+        for (Map.Entry<String, String> entry : user.entrySet()) {
+            args.add(entry.getKey());
+            args.add(entry.getValue());
+        }
+        args.add("--strip-debug");
+        args.add("--no-header-files");
+        args.add("--bind-services");
+        
+        StringWriter writer = new StringWriter();
+        PrintWriter pw = new PrintWriter(writer);
+
+        Log.verbose("jlink arguments: " + args);
+        int retVal = JLINK_TOOL.run(pw, pw, args.toArray(new String[0]));
+        String jlinkOut = writer.toString();
+
+        if (retVal != 0) {
+            throw new IOException("jlink failed with: " + jlinkOut);
+        } else if (jlinkOut.length() > 0) {
+            Log.verbose("jlink output: " + jlinkOut);
+        }
+    }
+
+    private static String getPathList(List<Path> pathList) {
+        String ret = null;
+        for (Path p : pathList) {
+            String s =  Matcher.quoteReplacement(p.toString());
+            if (ret == null) {
+                ret = s;
+            } else {
+                ret += File.pathSeparator +  s;
+            }
+        }
+        return ret;
+    }
+
+    private static String getStringList(Set<String> strings) {
+        String ret = null;
+        for (String s : strings) {
+            if (ret == null) {
+                ret = s;
+            } else {
+                ret += "," + s;
+            }
+        }
+        return (ret == null) ? null : Matcher.quoteReplacement(ret);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/JPackageToolProvider.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2017, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jpackage.internal;
+
+import java.io.PrintWriter;
+import java.util.spi.ToolProvider;
+
+/**
+ * JPackageToolProvider
+ *
+ * This is the ToolProvider implementation exported
+ * to java.util.spi.ToolProvider and ultimately javax.tools.ToolProvider
+ */
+public class JPackageToolProvider implements ToolProvider {
+
+    public String name() {
+        return "jpackage";
+    }
+
+    public synchronized int run(
+            PrintWriter out, PrintWriter err, String... args) {
+        try {
+            return jdk.jpackage.main.Main.run(out, err, args);
+        } catch (Exception ignored) {
+            return -1;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Log.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2011, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jpackage.internal;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+
+/**
+ * Log
+ *
+ * General purpose logging mechanism.
+ */
+public class Log {
+    public static class Logger {
+        private boolean verbose = false;
+        private PrintWriter out = null;
+        private PrintWriter err = null;
+
+        public Logger(boolean v) {
+            verbose = v;
+        }
+
+        public void setVerbose(boolean v) {
+            verbose = v;
+        }
+
+        public boolean isVerbose() {
+            return verbose;
+        }
+
+        public void setPrintWriter(PrintWriter out, PrintWriter err) {
+            this.out = out;
+            this.err = err;
+        }
+
+        public void flush() {
+            if (out != null) {
+                out.flush();
+            }
+
+            if (err != null) {
+                err.flush();
+            }
+        }
+
+        public void info(String msg) {
+            if (out != null) {
+                out.println(msg);
+            } else {
+                System.out.println(msg);
+            }
+        }
+
+        public void error(String msg) {
+            if (err != null) {
+                err.println(msg);
+            } else {
+                System.err.println(msg);
+            }
+        }
+
+        public void verbose(Throwable t) {
+            if (out != null && (Log.debug || verbose)) {
+                t.printStackTrace(out);
+            } else if (Log.debug || verbose) {
+                t.printStackTrace(System.out);
+            }
+        }
+
+        public void verbose(String msg) {
+            if (out != null && (Log.debug || verbose)) {
+                out.println(msg);
+            } else if (Log.debug || verbose) {
+                System.out.println(msg);
+            }
+        }
+
+        public void debug(String msg) {
+            if (out != null && Log.debug) {
+                out.println(msg);
+            } else if (Log.debug) {
+                System.out.println(msg);
+            }
+        }
+    }
+
+    private static Logger delegate = null;
+    private static boolean debug =
+            "true".equals(System.getenv("JPACKAGE_DEBUG"));
+
+    public static void setLogger(Logger l) {
+        delegate = l;
+        if (l == null) {
+            delegate = new Logger(false);
+        }
+    }
+
+    public static Logger getLogger() {
+        return delegate;
+    }
+
+    public static void flush() {
+        if (delegate != null) {
+            delegate.flush();
+        }
+    }
+
+    public static void info(String msg) {
+        if (delegate != null) {
+           delegate.info(msg);
+        }
+    }
+
+    public static void error(String msg) {
+        if (delegate != null) {
+            delegate.error(msg);
+        }
+    }
+
+    public static void setVerbose(boolean v) {
+        if (delegate != null) {
+            delegate.setVerbose(v);
+        }
+    }
+
+    public static boolean isVerbose() {
+        if (delegate != null) {
+            return delegate.isVerbose();
+        }
+
+        return false; // Off by default
+    }
+
+    public static void verbose(String msg) {
+        if (delegate != null) {
+           delegate.verbose(msg);
+        }
+    }
+
+    public static void verbose(Throwable t) {
+        if (delegate != null) {
+           delegate.verbose(t);
+        }
+    }
+
+    public static void debug(String msg) {
+        if (delegate != null) {
+           delegate.debug(msg);
+        }
+    }
+
+    public static void debug(Throwable t) {
+        try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
+            try (PrintStream ps = new PrintStream(baos)) {
+                t.printStackTrace(ps);
+            }
+            debug(baos.toString());
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    public static boolean isDebug() {
+        return debug;
+    }
+
+    public static void setDebug(boolean debug) {
+        Log.debug = debug;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ModFile.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jpackage.internal;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+final class ModFile {
+    private final String filename;
+    private final ModType moduleType;
+
+    enum JarType {All, UnnamedJar, ModularJar}
+    enum ModType {
+            Unknown, UnnamedJar, ModularJar, Jmod, ExplodedModule}
+
+    ModFile(File aFile) {
+        super();
+        filename = aFile.getPath();
+        moduleType = getModType(aFile);
+    }
+
+    String getModName() {
+        File file = new File(getFileName());
+        // do not try to remove extension for directories
+        return moduleType == ModType.ExplodedModule ?
+                file.getName() : getFileWithoutExtension(file.getName());
+    }
+
+    String getFileName() {
+        return filename;
+    }
+
+    ModType getModType() {
+        return moduleType;
+    }
+
+    private static ModType getModType(File aFile) {
+        ModType result = ModType.Unknown;
+        String filename = aFile.getAbsolutePath();
+
+        if (aFile.isFile()) {
+            if (filename.endsWith(".jmod")) {
+                result = ModType.Jmod;
+            }
+            else if (filename.endsWith(".jar")) {
+                JarType status = isModularJar(filename);
+
+                if (status == JarType.ModularJar) {
+                    result = ModType.ModularJar;
+                }
+                else if (status == JarType.UnnamedJar) {
+                    result = ModType.UnnamedJar;
+                }
+            }
+        }
+        else if (aFile.isDirectory()) {
+            File moduleInfo = new File(
+                    filename + File.separator + "module-info.class");
+
+            if (moduleInfo.exists()) {
+                result = ModType.ExplodedModule;
+            }
+        }
+
+        return result;
+    }
+
+    private static JarType isModularJar(String FileName) {
+        JarType result = JarType.All;
+
+        try {
+            ZipInputStream zip =
+                    new ZipInputStream(new FileInputStream(FileName));
+            result = JarType.UnnamedJar;
+
+            try {
+                for (ZipEntry entry = zip.getNextEntry(); entry != null;
+                        entry = zip.getNextEntry()) {
+                    if (entry.getName().matches("module-info.class")) {
+                        result = JarType.ModularJar;
+                        break;
+                    }
+                }
+
+                zip.close();
+            } catch (IOException ex) {
+            }
+        } catch (FileNotFoundException e) {
+        }
+
+        return result;
+    }
+
+    private static String getFileWithoutExtension(String FileName) {
+        return FileName.replaceFirst("[.][^.]+$", "");
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ModuleManager.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jpackage.internal;
+
+import java.io.File;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.EnumSet;
+import java.util.List;
+
+final class ModuleManager {
+    private final List<String> folders = new ArrayList<String>();
+
+    enum SearchType {UnnamedJar, ModularJar, Jmod, ExplodedModule}
+
+    ModuleManager(String folders) {
+        super();
+        String lfolders = folders.replaceAll("^\"|\"$", "");
+        List<Path> paths = new ArrayList<Path>();
+
+        for (String folder :
+                Arrays.asList(lfolders.split(File.pathSeparator))) {
+            File file = new File(folder);
+            paths.add(file.toPath());
+        }
+
+        initialize(paths);
+    }
+
+    ModuleManager(List<Path> Paths) {
+        super();
+        initialize(Paths);
+    }
+
+    private void initialize(List<Path> Paths) {
+        for (Path path : Paths) {
+            folders.add(path.toString().replaceAll("^\"|\"$", ""));
+        }
+    }
+
+    List<ModFile> getModules() {
+        return getModules(EnumSet.of(SearchType.UnnamedJar,
+                SearchType.ModularJar, SearchType.Jmod,
+                SearchType.ExplodedModule));
+    }
+
+    List<ModFile> getModules(EnumSet<SearchType> Search) {
+        List<ModFile> result = new ArrayList<ModFile>();
+
+        for (String folder : folders) {
+            result.addAll(getAllModulesInDirectory(folder, Search));
+        }
+
+        return result;
+    }
+
+    private static List<ModFile> getAllModulesInDirectory(String folder,
+            EnumSet<SearchType> Search) {
+        List<ModFile> result = new ArrayList<ModFile>();
+        File lfolder = new File(folder);
+        File[] files = { lfolder };
+        if (lfolder.isDirectory()) {
+            files = lfolder.listFiles();
+        }
+
+        if (files != null) {
+            for (File file : files) {
+                ModFile modFile = new ModFile(file);
+
+                switch (modFile.getModType()) {
+                    case Unknown:
+                        break;
+                    case UnnamedJar:
+                        if (Search.contains(SearchType.UnnamedJar)) {
+                            result.add(modFile);
+                        }
+                        break;
+                    case ModularJar:
+                        if (Search.contains(SearchType.ModularJar)) {
+                            result.add(modFile);
+                        }
+                        break;
+                    case Jmod:
+                        if (Search.contains(SearchType.Jmod)) {
+                            result.add(modFile);
+                        }
+                        break;
+                    case ExplodedModule:
+                        if (Search.contains(SearchType.ExplodedModule)) {
+                            result.add(modFile);
+                        }
+                        break;
+                }
+            }
+        }
+        return result;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/PackagerException.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2011, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jpackage.internal;
+
+import java.text.MessageFormat;
+import java.util.ResourceBundle;
+
+public class PackagerException extends Exception {
+    private static final long serialVersionUID = 1L;
+    private static final ResourceBundle bundle = ResourceBundle.getBundle(
+            "jdk.jpackage.internal.resources.MainResources");
+
+    public PackagerException(Throwable cause) {
+        super(cause);
+    }
+
+    public PackagerException(String key, Throwable cause) {
+        super(bundle.getString(key), cause);
+    }
+
+    public PackagerException(String key) {
+        super(bundle.getString(key));
+    }
+
+    public PackagerException(String key, String ... arguments) {
+        super(MessageFormat.format(
+                bundle.getString(key), (Object[]) arguments));
+    }
+
+    public PackagerException(
+            Throwable cause, String key, String ... arguments) {
+        super(MessageFormat.format(bundle.getString(key),
+                (Object[]) arguments), cause);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Param.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2011, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jpackage.internal;
+
+class Param {
+    String name;
+    String value;
+
+    void setName(String name) {
+        this.name = name;
+    }
+
+    void setValue(String value) {
+        this.value = value;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Platform.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jpackage.internal;
+
+import java.util.regex.Pattern;
+
+/**
+ * Platform
+ *
+ * Use <code>Platform</code> to detect the operating system
+ * that is currently running.
+ *
+ * Example:
+ *
+ *  Platform platform = Platform.getPlatform();
+ *
+ *  switch(platform) {
+ *    case Platform.MAC: {
+ *      // Do something
+ *      break;
+ *    }
+ *    case Platform.WINDOWS:
+ *    case Platform.LINUX: {
+ *      // Do something else
+ *    }
+ *  }
+ *
+ */
+enum Platform {UNKNOWN, WINDOWS, LINUX, MAC;
+    private static final Platform platform;
+    private static final int majorVersion;
+    private static final int minorVersion;
+
+    static {
+        String os = System.getProperty("os.name").toLowerCase();
+
+        if (os.indexOf("win") >= 0) {
+            platform = Platform.WINDOWS;
+        }
+        else if (os.indexOf("nix") >= 0 || os.indexOf("nux") >= 0) {
+            platform = Platform.LINUX;
+        }
+        else if (os.indexOf("mac") >= 0) {
+            platform = Platform.MAC;
+        }
+        else {
+            platform = Platform.UNKNOWN;
+        }
+
+        String version = System.getProperty("os.version").toString();
+        String[] parts = version.split(Pattern.quote("."));
+
+        if (parts.length > 0) {
+            majorVersion = Integer.parseInt(parts[0]);
+
+            if (parts.length > 1) {
+                minorVersion = Integer.parseInt(parts[1]);
+            }
+            else {
+                minorVersion = -1;
+            }
+        }
+        else {
+            majorVersion = -1;
+            minorVersion = -1;
+        }
+    }
+
+    private Platform() {}
+
+    static Platform getPlatform() {
+        return platform;
+    }
+
+    static int getMajorVersion() {
+        return majorVersion;
+    }
+
+    static int getMinorVersion() {
+        return minorVersion;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/RelativeFileSet.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2012, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jpackage.internal;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+/**
+ * RelativeFileSet
+ *
+ * A class encapsulating a directory and a set of files within it.
+ */
+class RelativeFileSet {
+
+    private File basedir;
+    private Set<String> files = new LinkedHashSet<>();
+
+    RelativeFileSet(RelativeFileSet copy) {
+        basedir = copy.basedir;
+        files = new LinkedHashSet<>(copy.files);
+    }
+
+    RelativeFileSet(File base, Collection<File> files) {
+        basedir = base;
+        String baseAbsolute = basedir.getAbsolutePath();
+        for (File f: files) {
+            String absolute = f.getAbsolutePath();
+            if (!absolute.startsWith(baseAbsolute)) {
+                throw new RuntimeException("File " + f.getAbsolutePath() +
+                        " does not belong to " + baseAbsolute);
+            }
+            if (!absolute.equals(baseAbsolute)) {
+                    // possible in jpackage case
+                this.files.add(absolute.substring(baseAbsolute.length()+1));
+            }
+        }
+    }
+
+    void upshift() {
+        String root = basedir.getName();
+        basedir = basedir.getParentFile();
+        Set<String> newFiles = new LinkedHashSet<>();
+        for (String s : files) {
+            newFiles.add(root + File.separator + s);
+        }
+        files = newFiles;
+    }
+
+    RelativeFileSet(File base, Set<File> files) {
+        this(base, (Collection<File>) files);
+    }
+
+    boolean contains(String[] requiredFiles) {
+        boolean result = true;
+
+        for(String fname: requiredFiles) {
+            if (!files.contains(fname)) {
+                Log.debug("  RelativeFileSet does not contain [" + fname + "]");
+                result = false;
+            }
+        }
+
+        return result;
+    }
+
+    boolean contains(String requiredFile) {
+        if (files.contains(requiredFile)) {
+            return true;
+        } else {
+            Log.debug("RelativeFileSet does not contain [" +requiredFile+ "]");
+            return false;
+        }
+    }
+
+    File getBaseDirectory() {
+        return basedir;
+    }
+
+    Set<String> getIncludedFiles() {
+        return files;
+    }
+
+    @Override
+    public String toString() {
+        return "RelativeFileSet {basedir:" + basedir
+                + ", files: {" + files + ")}";
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/SecondaryLauncherArguments.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jpackage.internal;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.io.File;
+import jdk.jpackage.internal.Arguments.CLIOptions;
+
+/*
+ * SecondaryLauncherArguments
+ *
+ * Processes a secondary launcher properties file to create the Map of
+ * bundle params applicable to the secondary launcher:
+ *
+ * BundlerParams p = (new SecondaryLauncherArguments(file)).getLauncherMap();
+ *
+ * A secondary launcher is another executable program generated by either the
+ * create-image mode or the create-installer mode.
+ * The secondary launcher may be the same program with different configuration,
+ * or a completely different program created from the same files.
+ *
+ * There may be multiple secondary launchers, each created by using the
+ * command line arg "--secondary-launcher <file path>
+ *
+ * The secondary launcher properties file may have any of:
+ *
+ * name (required)
+ * version
+ * module
+ * class
+ * icon
+ * arguments
+ * jvm-args
+ * win-menu
+ * win-shortcut
+ * win-console
+ *
+ */
+class SecondaryLauncherArguments {
+
+    private final String filename;
+    private Map<String, String> allArgs;
+    private Map<String, ? super Object> bundleParams;
+
+    SecondaryLauncherArguments(String filename) {
+        this.filename = filename;
+    }
+
+    private void initLauncherMap() {
+        if (bundleParams != null) {
+            return;
+        }
+
+        allArgs = Arguments.getPropertiesFromFile(filename);
+
+        bundleParams = new HashMap<>();
+        String mainClass = getOptionValue(CLIOptions.APPCLASS);
+        String module = getOptionValue(CLIOptions.MODULE);
+
+        if (module != null && mainClass != null) {
+            putUnlessNull(bundleParams, Arguments.CLIOptions.MODULE.getId(),
+                    module + "/" + mainClass);
+        } else if (module != null) {
+            putUnlessNull(bundleParams, Arguments.CLIOptions.MODULE.getId(),
+                    module);
+        } else if (mainClass != null) {
+            putUnlessNull(bundleParams, Arguments.CLIOptions.APPCLASS.getId(),
+                    mainClass);
+        }
+
+        putUnlessNull(bundleParams, Arguments.CLIOptions.NAME.getId(),
+                getOptionValue(CLIOptions.NAME));
+        putUnlessNull(bundleParams, Arguments.CLIOptions.VERSION.getId(),
+                getOptionValue(CLIOptions.VERSION));
+
+        putUnlessNull(bundleParams, Arguments.CLIOptions.WIN_MENU_HINT.getId(),
+                getOptionValue(CLIOptions.WIN_MENU_HINT));
+        putUnlessNull(bundleParams,
+                Arguments.CLIOptions.WIN_SHORTCUT_HINT.getId(),
+                getOptionValue(CLIOptions.WIN_SHORTCUT_HINT));
+        putUnlessNull(bundleParams,
+                Arguments.CLIOptions.WIN_CONSOLE_HINT.getId(),
+                getOptionValue(CLIOptions.WIN_CONSOLE_HINT));
+
+        String value = getOptionValue(CLIOptions.ICON);
+        putUnlessNull(bundleParams, Arguments.CLIOptions.ICON.getId(),
+                (value == null) ? null : new File(value));
+
+        String argumentStr = getOptionValue(CLIOptions.ARGUMENTS);
+        putUnlessNullOrEmpty(bundleParams,
+                CLIOptions.ARGUMENTS.getId(),
+                Arguments.getArgumentList(argumentStr));
+
+        String jvmargsStr = getOptionValue(CLIOptions.JVM_ARGS);
+        putUnlessNullOrEmpty(bundleParams,
+                CLIOptions.JVM_ARGS.getId(),
+                Arguments.getArgumentList(jvmargsStr));
+    }
+
+    private String getOptionValue(CLIOptions option) {
+        if (option == null || allArgs == null) {
+            return null;
+        }
+
+        String id = option.getId();
+
+        if (allArgs.containsKey(id)) {
+            return allArgs.get(id);
+        }
+
+        return null;
+    }
+
+    Map<String, ? super Object> getLauncherMap() {
+        initLauncherMap();
+        return bundleParams;
+    }
+
+    private void putUnlessNull(Map<String, ? super Object> params,
+            String param, Object value) {
+        if (value != null) {
+            params.put(param, value);
+        }
+    }
+
+    private void putUnlessNullOrEmpty(Map<String, ? super Object> params,
+            String param, Collection<?> value) {
+        if (value != null && !value.isEmpty()) {
+            params.put(param, value);
+        }
+    }
+
+    private void putUnlessNullOrEmpty(Map<String, ? super Object> params,
+            String param, Map<?, ?> value) {
+        if (value != null && !value.isEmpty()) {
+            params.put(param, value);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/StandardBundlerParam.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,902 @@
+/*
+ * Copyright (c) 2014, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jpackage.internal;
+
+import jdk.jpackage.internal.BundleParams;
+import jdk.jpackage.internal.AbstractAppImageBuilder;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.StringReader;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Properties;
+import java.util.ResourceBundle;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+import java.util.jar.Attributes;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+/**
+ * StandardBundlerParams
+ *
+ * A parameter to a bundler.
+ *
+ * Also contains static definitions of all of the common bundler parameters.
+ * (additional platform specific and mode specific bundler parameters
+ * are defined in each of the specific bundlers)
+ *
+ * Also contains static methods that operate on maps of parameters.
+ */
+class StandardBundlerParam<T> extends BundlerParamInfo<T> {
+
+    private static final ResourceBundle I18N = ResourceBundle.getBundle(
+            "jdk.jpackage.internal.resources.MainResources");
+    private static final String JAVABASEJMOD = "java.base.jmod";
+
+    StandardBundlerParam(String name, String description, String id,
+            Class<T> valueType,
+            Function<Map<String, ? super Object>, T> defaultValueFunction,
+            BiFunction<String, Map<String, ? super Object>, T> stringConverter)
+    {
+        this.name = name;
+        this.description = description;
+        this.id = id;
+        this.valueType = valueType;
+        this.defaultValueFunction = defaultValueFunction;
+        this.stringConverter = stringConverter;
+    }
+
+    static final StandardBundlerParam<RelativeFileSet> APP_RESOURCES =
+            new StandardBundlerParam<>(
+                    I18N.getString("param.app-resources.name"),
+                    I18N.getString("param.app-resource.description"),
+                    BundleParams.PARAM_APP_RESOURCES,
+                    RelativeFileSet.class,
+                    null, // no default.  Required parameter
+                    null  // no string translation,
+                          // tool must provide complex type
+            );
+
+    @SuppressWarnings("unchecked")
+    static final
+            StandardBundlerParam<List<RelativeFileSet>> APP_RESOURCES_LIST =
+            new StandardBundlerParam<>(
+                    I18N.getString("param.app-resources-list.name"),
+                    I18N.getString("param.app-resource-list.description"),
+                    BundleParams.PARAM_APP_RESOURCES + "List",
+                    (Class<List<RelativeFileSet>>) (Object) List.class,
+                    // Default is appResources, as a single item list
+                    p -> new ArrayList<>(Collections.singletonList(
+                            APP_RESOURCES.fetchFrom(p))),
+                    StandardBundlerParam::createAppResourcesListFromString
+            );
+
+    static final StandardBundlerParam<String> SOURCE_DIR =
+            new StandardBundlerParam<>(
+                    I18N.getString("param.source-dir.name"),
+                    I18N.getString("param.source-dir.description"),
+                    Arguments.CLIOptions.INPUT.getId(),
+                    String.class,
+                    p -> null,
+                    (s, p) -> {
+                        String value = String.valueOf(s);
+                        if (value.charAt(value.length() - 1) ==
+                                File.separatorChar) {
+                            return value.substring(0, value.length() - 1);
+                        }
+                        else {
+                            return value;
+                        }
+                    }
+            );
+
+    // note that each bundler is likely to replace this one with
+    // their own converter
+    static final StandardBundlerParam<RelativeFileSet> MAIN_JAR =
+            new StandardBundlerParam<>(
+                    I18N.getString("param.main-jar.name"),
+                    I18N.getString("param.main-jar.description"),
+                    Arguments.CLIOptions.MAIN_JAR.getId(),
+                    RelativeFileSet.class,
+                    params -> {
+                        extractMainClassInfoFromAppResources(params);
+                        return (RelativeFileSet) params.get("mainJar");
+                    },
+                    (s, p) -> getMainJar(s, p)
+            );
+
+    // TODO: test CLASSPATH jar manifest Attributet
+    static final StandardBundlerParam<String> CLASSPATH =
+            new StandardBundlerParam<>(
+                    I18N.getString("param.classpath.name"),
+                    I18N.getString("param.classpath.description"),
+                    "classpath",
+                    String.class,
+                    params -> {
+                        extractMainClassInfoFromAppResources(params);
+                        String cp = (String) params.get("classpath");
+                        return cp == null ? "" : cp;
+                    },
+                    (s, p) -> s.replace(File.pathSeparator, " ")
+            );
+
+    static final StandardBundlerParam<Boolean> RUNTIME_INSTALLER  =
+            new StandardBundlerParam<>(
+                    "",
+                    "",
+                    Arguments.CLIOptions.RUNTIME_INSTALLER.getId(),
+                    Boolean.class,
+                    params -> false,
+                    // valueOf(null) is false, and we actually do want null
+                    (s, p) -> (s == null || "null".equalsIgnoreCase(s)) ?
+                            true : Boolean.valueOf(s)
+            );
+
+
+    static final StandardBundlerParam<String> MAIN_CLASS =
+            new StandardBundlerParam<>(
+                    I18N.getString("param.main-class.name"),
+                    I18N.getString("param.main-class.description"),
+                    Arguments.CLIOptions.APPCLASS.getId(),
+                    String.class,
+                    params -> {
+                        if (RUNTIME_INSTALLER.fetchFrom(params)) {
+                            return null;
+                        }
+                        extractMainClassInfoFromAppResources(params);
+                        String s = (String) params.get(
+                                BundleParams.PARAM_APPLICATION_CLASS);
+                        if (s == null) {
+                            s = JLinkBundlerHelper.getMainClass(params);
+                        }
+                        return s;
+                    },
+                    (s, p) -> s
+            );
+
+    static final StandardBundlerParam<String> APP_NAME =
+            new StandardBundlerParam<>(
+                    I18N.getString("param.app-name.name"),
+                    I18N.getString("param.app-name.description"),
+                    Arguments.CLIOptions.NAME.getId(),
+                    String.class,
+                    params -> {
+                        String s = MAIN_CLASS.fetchFrom(params);
+                        if (s == null) return null;
+
+                        int idx = s.lastIndexOf(".");
+                        if (idx >= 0) {
+                            return s.substring(idx+1);
+                        }
+                        return s;
+                    },
+                    (s, p) -> s
+            );
+
+    static final StandardBundlerParam<File> ICON =
+            new StandardBundlerParam<>(
+                    I18N.getString("param.icon-file.name"),
+                    I18N.getString("param.icon-file.description"),
+                    Arguments.CLIOptions.ICON.getId(),
+                    File.class,
+                    params -> null,
+                    (s, p) -> new File(s)
+            );
+
+    static final StandardBundlerParam<String> VENDOR =
+            new StandardBundlerParam<>(
+                    I18N.getString("param.vendor.name"),
+                    I18N.getString("param.vendor.description"),
+                    Arguments.CLIOptions.VENDOR.getId(),
+                    String.class,
+                    params -> I18N.getString("param.vendor.default"),
+                    (s, p) -> s
+            );
+
+    static final StandardBundlerParam<String> CATEGORY =
+            new StandardBundlerParam<>(
+                    I18N.getString("param.category.name"),
+                    I18N.getString("param.category.description"),
+                   Arguments.CLIOptions.CATEGORY.getId(),
+                    String.class,
+                    params -> I18N.getString("param.category.default"),
+                    (s, p) -> s
+            );
+
+    static final StandardBundlerParam<String> DESCRIPTION =
+            new StandardBundlerParam<>(
+                    I18N.getString("param.description.name"),
+                    I18N.getString("param.description.description"),
+                    Arguments.CLIOptions.DESCRIPTION.getId(),
+                    String.class,
+                    params -> params.containsKey(APP_NAME.getID())
+                            ? APP_NAME.fetchFrom(params)
+                            : I18N.getString("param.description.default"),
+                    (s, p) -> s
+            );
+
+    static final StandardBundlerParam<String> COPYRIGHT =
+            new StandardBundlerParam<>(
+                    I18N.getString("param.copyright.name"),
+                    I18N.getString("param.copyright.description"),
+                    Arguments.CLIOptions.COPYRIGHT.getId(),
+                    String.class,
+                    params -> MessageFormat.format(I18N.getString(
+                            "param.copyright.default"), new Date()),
+                    (s, p) -> s
+            );
+
+    @SuppressWarnings("unchecked")
+    static final StandardBundlerParam<List<String>> ARGUMENTS =
+            new StandardBundlerParam<>(
+                    I18N.getString("param.arguments.name"),
+                    I18N.getString("param.arguments.description"),
+                    Arguments.CLIOptions.ARGUMENTS.getId(),
+                    (Class<List<String>>) (Object) List.class,
+                    params -> Collections.emptyList(),
+                    (s, p) -> splitStringWithEscapes(s)
+            );
+
+    @SuppressWarnings("unchecked")
+    static final StandardBundlerParam<List<String>> JVM_OPTIONS =
+            new StandardBundlerParam<>(
+                    I18N.getString("param.jvm-options.name"),
+                    I18N.getString("param.jvm-options.description"),
+                    Arguments.CLIOptions.JVM_ARGS.getId(),
+                    (Class<List<String>>) (Object) List.class,
+                    params -> Collections.emptyList(),
+                    (s, p) -> Arrays.asList(s.split("\n\n"))
+            );
+
+    static final StandardBundlerParam<String> TITLE =
+            new StandardBundlerParam<>(
+                    I18N.getString("param.title.name"),
+                    I18N.getString("param.title.description"),
+                    BundleParams.PARAM_TITLE,
+                    String.class,
+                    APP_NAME::fetchFrom,
+                    (s, p) -> s
+            );
+
+    // note that each bundler is likely to replace this one with
+    // their own converter
+    static final StandardBundlerParam<String> VERSION =
+            new StandardBundlerParam<>(
+                    I18N.getString("param.version.name"),
+                    I18N.getString("param.version.description"),
+                    Arguments.CLIOptions.VERSION.getId(),
+                    String.class,
+                    params -> I18N.getString("param.version.default"),
+                    (s, p) -> s
+            );
+
+    @SuppressWarnings("unchecked")
+    public static final StandardBundlerParam<String> LICENSE_FILE =
+            new StandardBundlerParam<>(
+                    I18N.getString("param.license-file.name"),
+                    I18N.getString("param.license-file.description"),
+                    Arguments.CLIOptions.LICENSE_FILE.getId(),
+                    String.class,
+                    params -> null,
+                    (s, p) -> s
+            );
+
+    static final StandardBundlerParam<File> BUILD_ROOT =
+            new StandardBundlerParam<>(
+                    I18N.getString("param.build-root.name"),
+                    I18N.getString("param.build-root.description"),
+                    Arguments.CLIOptions.BUILD_ROOT.getId(),
+                    File.class,
+                    params -> {
+                        try {
+                            return Files.createTempDirectory(
+                                    "jdk.jpackage").toFile();
+                        } catch (IOException ioe) {
+                            return null;
+                        }
+                    },
+                    (s, p) -> new File(s)
+            );
+
+    public static final StandardBundlerParam<File> CONFIG_ROOT =
+            new StandardBundlerParam<>(
+                I18N.getString("param.config-root.name"),
+                I18N.getString("param.config-root.description"),
+                "configRoot",
+                File.class,
+                params -> {
+                    File root =
+                            new File(BUILD_ROOT.fetchFrom(params), "config");
+                    root.mkdirs();
+                    return root;
+                },
+                (s, p) -> null
+            );
+
+    static final StandardBundlerParam<String> IDENTIFIER =
+            new StandardBundlerParam<>(
+                    I18N.getString("param.identifier.name"),
+                    I18N.getString("param.identifier.description"),
+                    Arguments.CLIOptions.IDENTIFIER.getId(),
+                    String.class,
+                    params -> {
+                        String s = MAIN_CLASS.fetchFrom(params);
+                        if (s == null) return null;
+
+                        int idx = s.lastIndexOf(".");
+                        if (idx >= 1) {
+                            return s.substring(0, idx);
+                        }
+                        return s;
+                    },
+                    (s, p) -> s
+            );
+
+    static final StandardBundlerParam<String> PREFERENCES_ID =
+            new StandardBundlerParam<>(
+                    I18N.getString("param.preferences-id.name"),
+                    I18N.getString("param.preferences-id.description"),
+                    "preferencesID",
+                    String.class,
+                    p -> Optional.ofNullable(IDENTIFIER.fetchFrom(p)).
+                             orElse("").replace('.', '/'),
+                    (s, p) -> s
+            );
+
+    static final StandardBundlerParam<Boolean> VERBOSE  =
+            new StandardBundlerParam<>(
+                    I18N.getString("param.verbose.name"),
+                    I18N.getString("param.verbose.description"),
+                    Arguments.CLIOptions.VERBOSE.getId(),
+                    Boolean.class,
+                    params -> false,
+                    // valueOf(null) is false, and we actually do want null
+                    (s, p) -> (s == null || "null".equalsIgnoreCase(s)) ?
+                            true : Boolean.valueOf(s)
+            );
+
+    static final StandardBundlerParam<Boolean> OVERWRITE  =
+            new StandardBundlerParam<>(
+                    I18N.getString("param.overwrite.name"),
+                    I18N.getString("param.overwrite.description"),
+                    Arguments.CLIOptions.OVERWRITE.getId(),
+                    Boolean.class,
+                    params -> false,
+                    // valueOf(null) is false, and we actually do want null
+                    (s, p) -> (s == null || "null".equalsIgnoreCase(s)) ?
+                            true : Boolean.valueOf(s)
+            );
+
+    static final StandardBundlerParam<File> RESOURCE_DIR =
+            new StandardBundlerParam<>(
+                    I18N.getString("param.resource-dir.name"),
+                    I18N.getString("param.resource-dir.description"),
+                    Arguments.CLIOptions.RESOURCE_DIR.getId(),
+                    File.class,
+                    params -> null,
+                    (s, p) -> new File(s)
+            );
+
+    static final BundlerParamInfo<String> INSTALL_DIR =
+            new StandardBundlerParam<>(
+                    I18N.getString("param.install-dir.name"),
+                    I18N.getString("param.install-dir.description"),
+                    Arguments.CLIOptions.INSTALL_DIR.getId(),
+                    String.class,
+                     params -> null,
+                    (s, p) -> s
+    );
+
+    static final StandardBundlerParam<File> PREDEFINED_APP_IMAGE =
+            new StandardBundlerParam<>(
+            I18N.getString("param.predefined-app-image.name"),
+            I18N.getString("param.predefined-app-image.description"),
+            Arguments.CLIOptions.PREDEFINED_APP_IMAGE.getId(),
+            File.class,
+            params -> null,
+            (s, p) -> new File(s));
+
+    static final StandardBundlerParam<File> PREDEFINED_RUNTIME_IMAGE =
+            new StandardBundlerParam<>(
+            I18N.getString("param.predefined-runtime-image.name"),
+            I18N.getString("param.predefined-runtime-image.description"),
+            Arguments.CLIOptions.PREDEFINED_RUNTIME_IMAGE.getId(),
+            File.class,
+            params -> null,
+            (s, p) -> new File(s));
+
+    @SuppressWarnings("unchecked")
+    static final StandardBundlerParam<List<Map<String, ? super Object>>> SECONDARY_LAUNCHERS =
+            new StandardBundlerParam<>(
+                    I18N.getString("param.secondary-launchers.name"),
+                    I18N.getString("param.secondary-launchers.description"),
+                    Arguments.CLIOptions.SECONDARY_LAUNCHER.getId(),
+                    (Class<List<Map<String, ? super Object>>>) (Object)
+                            List.class,
+                    params -> new ArrayList<>(1),
+                    // valueOf(null) is false, and we actually do want null
+                    (s, p) -> null
+            );
+
+    @SuppressWarnings("unchecked")
+    static final StandardBundlerParam
+            <List<Map<String, ? super Object>>> FILE_ASSOCIATIONS =
+            new StandardBundlerParam<>(
+                    I18N.getString("param.file-associations.name"),
+                    I18N.getString("param.file-associations.description"),
+                    Arguments.CLIOptions.FILE_ASSOCIATIONS.getId(),
+                    (Class<List<Map<String, ? super Object>>>) (Object)
+                            List.class,
+                    params -> new ArrayList<>(1),
+                    // valueOf(null) is false, and we actually do want null
+                    (s, p) -> null
+            );
+
+    @SuppressWarnings("unchecked")
+    static final StandardBundlerParam<List<String>> FA_EXTENSIONS =
+            new StandardBundlerParam<>(
+                    I18N.getString("param.fa-extension.name"),
+                    I18N.getString("param.fa-extension.description"),
+                    "fileAssociation.extension",
+                    (Class<List<String>>) (Object) List.class,
+                    params -> null, // null means not matched to an extension
+                    (s, p) -> Arrays.asList(s.split("(,|\\s)+"))
+            );
+
+    @SuppressWarnings("unchecked")
+    static final StandardBundlerParam<List<String>> FA_CONTENT_TYPE =
+            new StandardBundlerParam<>(
+                    I18N.getString("param.fa-content-type.name"),
+                    I18N.getString("param.fa-content-type.description"),
+                    "fileAssociation.contentType",
+                    (Class<List<String>>) (Object) List.class,
+                    params -> null,
+                            // null means not matched to a content/mime type
+                    (s, p) -> Arrays.asList(s.split("(,|\\s)+"))
+            );
+
+    static final StandardBundlerParam<String> FA_DESCRIPTION =
+            new StandardBundlerParam<>(
+                    I18N.getString("param.fa-description.name"),
+                    I18N.getString("param.fa-description.description"),
+                    "fileAssociation.description",
+                    String.class,
+                    params -> APP_NAME.fetchFrom(params) + " File",
+                    null
+            );
+
+    static final StandardBundlerParam<File> FA_ICON =
+            new StandardBundlerParam<>(
+                    I18N.getString("param.fa-icon.name"),
+                    I18N.getString("param.fa-icon.description"),
+                    "fileAssociation.icon",
+                    File.class,
+                    ICON::fetchFrom,
+                    (s, p) -> new File(s)
+            );
+
+    @SuppressWarnings("unchecked")
+    static final BundlerParamInfo<List<Path>> MODULE_PATH =
+            new StandardBundlerParam<>(
+                    I18N.getString("param.module-path.name"),
+                    I18N.getString("param.module-path.description"),
+                    Arguments.CLIOptions.MODULE_PATH.getId(),
+                    (Class<List<Path>>) (Object)List.class,
+                    p -> { return getDefaultModulePath(); },
+                    (s, p) -> {
+                        List<Path> modulePath = Arrays.asList(s
+                                .split(File.pathSeparator)).stream()
+                                .map(ss -> new File(ss).toPath())
+                                .collect(Collectors.toList());
+                        Path javaBasePath = null;
+                        if (modulePath != null) {
+                            javaBasePath = JLinkBundlerHelper
+                                    .findPathOfModule(modulePath, JAVABASEJMOD);
+                        } else {
+                            modulePath = new ArrayList<Path>();
+                        }
+
+                        // Add the default JDK module path to the module path.
+                        if (javaBasePath == null) {
+                            List<Path> jdkModulePath = getDefaultModulePath();
+
+                            if (jdkModulePath != null) {
+                                modulePath.addAll(jdkModulePath);
+                                javaBasePath =
+                                        JLinkBundlerHelper.findPathOfModule(
+                                        modulePath, JAVABASEJMOD);
+                            }
+                        }
+
+                        if (javaBasePath == null ||
+                                !Files.exists(javaBasePath)) {
+                            Log.error(String.format(I18N.getString(
+                                    "warning.no.jdk.modules.found")));
+                        }
+
+                        return modulePath;
+                    });
+
+    static final BundlerParamInfo<String> MODULE =
+            new StandardBundlerParam<>(
+                    I18N.getString("param.main.module.name"),
+                    I18N.getString("param.main.module.description"),
+                    Arguments.CLIOptions.MODULE.getId(),
+                    String.class,
+                    p -> null,
+                    (s, p) -> {
+                        return String.valueOf(s);
+                    });
+
+    @SuppressWarnings("unchecked")
+    static final BundlerParamInfo<Set<String>> ADD_MODULES =
+            new StandardBundlerParam<>(
+                    I18N.getString("param.add-modules.name"),
+                    I18N.getString("param.add-modules.description"),
+                    Arguments.CLIOptions.ADD_MODULES.getId(),
+                    (Class<Set<String>>) (Object) Set.class,
+                    p -> new LinkedHashSet<String>(),
+                    (s, p) -> new LinkedHashSet<>(Arrays.asList(s.split(",")))
+            );
+
+    @SuppressWarnings("unchecked")
+    static final BundlerParamInfo<Set<String>> LIMIT_MODULES =
+            new StandardBundlerParam<>(
+                    I18N.getString("param.limit-modules.name"),
+                    I18N.getString("param.limit-modules.description"),
+                    "limit-modules",
+                    (Class<Set<String>>) (Object) Set.class,
+                    p -> new LinkedHashSet<String>(),
+                    (s, p) -> new LinkedHashSet<>(Arrays.asList(s.split(",")))
+            );
+
+    static final BundlerParamInfo<Boolean> STRIP_NATIVE_COMMANDS =
+            new StandardBundlerParam<>(
+                    I18N.getString("param.strip-executables.name"),
+                    I18N.getString("param.strip-executables.description"),
+                    Arguments.CLIOptions.STRIP_NATIVE_COMMANDS.getId(),
+                    Boolean.class,
+                    p -> Boolean.FALSE,
+                    (s, p) -> Boolean.valueOf(s)
+            );
+
+    static File getPredefinedAppImage(Map<String, ? super Object> p) {
+        File applicationImage = null;
+        if (PREDEFINED_APP_IMAGE.fetchFrom(p) != null) {
+            applicationImage = PREDEFINED_APP_IMAGE.fetchFrom(p);
+            Log.debug("Using App Image from " + applicationImage);
+            if (!applicationImage.exists()) {
+                throw new RuntimeException(
+                        MessageFormat.format(I18N.getString(
+                                "message.app-image-dir-does-not-exist"),
+                                PREDEFINED_APP_IMAGE.getID(),
+                                applicationImage.toString()));
+            }
+        }
+        return applicationImage;
+    }
+
+    static void copyPredefinedRuntimeImage(
+            Map<String, ? super Object> p,
+            AbstractAppImageBuilder appBuilder)
+            throws IOException , ConfigException {
+        File image = PREDEFINED_RUNTIME_IMAGE.fetchFrom(p);
+        if (!image.exists()) {
+            throw new ConfigException(
+                    MessageFormat.format(I18N.getString(
+                    "message.runtime-image-dir-does-not-exist"),
+                    PREDEFINED_RUNTIME_IMAGE.getID(),
+                    image.toString()),
+                    MessageFormat.format(I18N.getString(
+                    "message.runtime-image-dir-does-not-exist.advice"),
+                    PREDEFINED_RUNTIME_IMAGE.getID()));
+        }
+        // copy whole runtime, need to skip jmods and src.zip
+        final List<String> excludes = Arrays.asList("jmods", "src.zip");
+        IOUtils.copyRecursive(image.toPath(), appBuilder.getRoot(), excludes);
+
+        // if module-path given - copy modules to appDir/mods
+        List<Path> modulePath =
+                StandardBundlerParam.MODULE_PATH.fetchFrom(p);
+        List<Path> defaultModulePath = getDefaultModulePath();
+        Path dest = appBuilder.getAppModsDir();
+
+        if (dest != null) {
+            for (Path mp : modulePath) {
+                if (!defaultModulePath.contains(mp)) {
+                    Files.createDirectories(dest);
+                    IOUtils.copyRecursive(mp, dest);
+                }
+            }
+        }
+
+        appBuilder.prepareApplicationFiles();
+    }
+
+    static void extractMainClassInfoFromAppResources(
+            Map<String, ? super Object> params) {
+        boolean hasMainClass = params.containsKey(MAIN_CLASS.getID());
+        boolean hasMainJar = params.containsKey(MAIN_JAR.getID());
+        boolean hasMainJarClassPath = params.containsKey(CLASSPATH.getID());
+        boolean hasModule = params.containsKey(MODULE.getID());
+        boolean runtimeInstaller =
+                params.containsKey(RUNTIME_INSTALLER.getID());
+
+        if (hasMainClass && hasMainJar && hasMainJarClassPath || hasModule ||
+                runtimeInstaller) {
+            return;
+        }
+
+        // it's a pair.
+        // The [0] is the srcdir [1] is the file relative to sourcedir
+        List<String[]> filesToCheck = new ArrayList<>();
+
+        if (hasMainJar) {
+            RelativeFileSet rfs = MAIN_JAR.fetchFrom(params);
+            for (String s : rfs.getIncludedFiles()) {
+                filesToCheck.add(
+                        new String[] {rfs.getBaseDirectory().toString(), s});
+            }
+        } else if (hasMainJarClassPath) {
+            for (String s : CLASSPATH.fetchFrom(params).split("\\s+")) {
+                if (APP_RESOURCES.fetchFrom(params) != null) {
+                    filesToCheck.add(
+                            new String[] {APP_RESOURCES.fetchFrom(params)
+                            .getBaseDirectory().toString(), s});
+                }
+            }
+        } else {
+            List<RelativeFileSet> rfsl = APP_RESOURCES_LIST.fetchFrom(params);
+            if (rfsl == null || rfsl.isEmpty()) {
+                return;
+            }
+            for (RelativeFileSet rfs : rfsl) {
+                if (rfs == null) continue;
+
+                for (String s : rfs.getIncludedFiles()) {
+                    filesToCheck.add(
+                            new String[]{rfs.getBaseDirectory().toString(), s});
+                }
+            }
+        }
+
+        // presume the set iterates in-order
+        for (String[] fnames : filesToCheck) {
+            try {
+                // only sniff jars
+                if (!fnames[1].toLowerCase().endsWith(".jar")) continue;
+
+                File file = new File(fnames[0], fnames[1]);
+                // that actually exist
+                if (!file.exists()) continue;
+
+                try (JarFile jf = new JarFile(file)) {
+                    Manifest m = jf.getManifest();
+                    Attributes attrs = (m != null) ?
+                            m.getMainAttributes() : null;
+
+                    if (attrs != null) {
+                        if (!hasMainJar) {
+                            if (fnames[0] == null) {
+                                fnames[0] = file.getParentFile().toString();
+                            }
+                            params.put(MAIN_JAR.getID(), new RelativeFileSet(
+                                    new File(fnames[0]),
+                                    new LinkedHashSet<>(Collections
+                                    .singletonList(file))));
+                        }
+                        if (!hasMainJarClassPath) {
+                            String cp =
+                                    attrs.getValue(Attributes.Name.CLASS_PATH);
+                            params.put(CLASSPATH.getID(),
+                                    cp == null ? "" : cp);
+                        }
+                        break;
+                    }
+                }
+            } catch (IOException ignore) {
+                ignore.printStackTrace();
+            }
+        }
+    }
+
+    static void validateMainClassInfoFromAppResources(
+            Map<String, ? super Object> params) throws ConfigException {
+        boolean hasMainClass = params.containsKey(MAIN_CLASS.getID());
+        boolean hasMainJar = params.containsKey(MAIN_JAR.getID());
+        boolean hasMainJarClassPath = params.containsKey(CLASSPATH.getID());
+        boolean hasModule = params.containsKey(MODULE.getID());
+        boolean hasAppImage = params.containsKey(PREDEFINED_APP_IMAGE.getID());
+        boolean runtimeInstaller =
+                params.containsKey(RUNTIME_INSTALLER.getID());
+
+        if (hasMainClass && hasMainJar && hasMainJarClassPath ||
+               hasModule || runtimeInstaller || hasAppImage) {
+            return;
+        }
+
+        extractMainClassInfoFromAppResources(params);
+
+        if (!params.containsKey(MAIN_CLASS.getID())) {
+            if (hasMainJar) {
+                throw new ConfigException(
+                        MessageFormat.format(I18N.getString(
+                        "error.no-main-class-with-main-jar"),
+                        MAIN_JAR.fetchFrom(params)),
+                        MessageFormat.format(I18N.getString(
+                        "error.no-main-class-with-main-jar.advice"),
+                        MAIN_JAR.fetchFrom(params)));
+            } else {
+                throw new ConfigException(
+                        I18N.getString("error.no-main-class"),
+                        I18N.getString("error.no-main-class.advice"));
+            }
+        }
+    }
+
+
+    private static List<String> splitStringWithEscapes(String s) {
+        List<String> l = new ArrayList<>();
+        StringBuilder current = new StringBuilder();
+        boolean quoted = false;
+        boolean escaped = false;
+        for (char c : s.toCharArray()) {
+            if (escaped) {
+                current.append(c);
+            } else if ('"' == c) {
+                quoted = !quoted;
+            } else if (!quoted && Character.isWhitespace(c)) {
+                l.add(current.toString());
+                current = new StringBuilder();
+            } else {
+                current.append(c);
+            }
+        }
+        l.add(current.toString());
+        return l;
+    }
+
+    private static List<RelativeFileSet>
+            createAppResourcesListFromString(String s,
+            Map<String, ? super Object> objectObjectMap) {
+        List<RelativeFileSet> result = new ArrayList<>();
+        for (String path : s.split("[:;]")) {
+            File f = new File(path);
+            if (f.getName().equals("*") || path.endsWith("/") ||
+                    path.endsWith("\\")) {
+                if (f.getName().equals("*")) {
+                    f = f.getParentFile();
+                }
+                Set<File> theFiles = new HashSet<>();
+                try {
+                    Files.walk(f.toPath())
+                            .filter(Files::isRegularFile)
+                            .forEach(p -> theFiles.add(p.toFile()));
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+                result.add(new RelativeFileSet(f, theFiles));
+            } else {
+                result.add(new RelativeFileSet(f.getParentFile(),
+                        Collections.singleton(f)));
+            }
+        }
+        return result;
+    }
+
+    private static RelativeFileSet getMainJar(
+            String mainJarValue, Map<String, ? super Object> params) {
+        for (RelativeFileSet rfs : APP_RESOURCES_LIST.fetchFrom(params)) {
+            File appResourcesRoot = rfs.getBaseDirectory();
+            File mainJarFile = new File(appResourcesRoot, mainJarValue);
+
+            if (mainJarFile.exists()) {
+                return new RelativeFileSet(appResourcesRoot,
+                     new LinkedHashSet<>(Collections.singletonList(
+                     mainJarFile)));
+            }
+            mainJarFile = new File(mainJarValue);
+            if (mainJarFile.exists()) {
+                // absolute path for main-jar may fail is only legal if
+                // path is within the appResourceRoot directory
+                try {
+                    return new RelativeFileSet(appResourcesRoot,
+                         new LinkedHashSet<>(Collections.singletonList(
+                         mainJarFile)));
+                } catch (Exception e) {
+                    // if not within, RelativeFileSet constructor throws a
+                    // RuntimeException, but the IllegalArgumentException
+                    // below contains a more explicit error message.
+                }
+            } else {
+                List<Path> modulePath = MODULE_PATH.fetchFrom(params);
+                modulePath.removeAll(getDefaultModulePath());
+                if (!modulePath.isEmpty()) {
+                    Path modularJarPath = JLinkBundlerHelper.findPathOfModule(
+                            modulePath, mainJarValue);
+                    if (modularJarPath != null &&
+                            Files.exists(modularJarPath)) {
+                        return new RelativeFileSet(appResourcesRoot,
+                                new LinkedHashSet<>(Collections.singletonList(
+                                modularJarPath.toFile())));
+                    }
+                }
+            }
+        }
+
+        throw new IllegalArgumentException(
+                new ConfigException(MessageFormat.format(I18N.getString(
+                        "error.main-jar-does-not-exist"),
+                        mainJarValue), I18N.getString(
+                        "error.main-jar-does-not-exist.advice")));
+    }
+
+    static List<Path> getDefaultModulePath() {
+        List<Path> result = new ArrayList<Path>();
+        Path jdkModulePath = Paths.get(
+                System.getProperty("java.home"), "jmods").toAbsolutePath();
+
+        if (jdkModulePath != null && Files.exists(jdkModulePath)) {
+            result.add(jdkModulePath);
+        }
+        else {
+            // On a developer build the JDK Home isn't where we expect it
+            // relative to the jmods directory. Do some extra
+            // processing to find it.
+            Map<String, String> env = System.getenv();
+
+            if (env.containsKey("JDK_HOME")) {
+                jdkModulePath = Paths.get(env.get("JDK_HOME"),
+                        ".." + File.separator + "images"
+                        + File.separator + "jmods").toAbsolutePath();
+
+                if (jdkModulePath != null && Files.exists(jdkModulePath)) {
+                    result.add(jdkModulePath);
+                }
+            }
+        }
+
+        return result;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/UnsupportedPlatformException.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2012, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jpackage.internal;
+
+public class UnsupportedPlatformException extends Exception {
+    private static final long serialVersionUID = 1L;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ValidOptions.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jpackage.internal;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import jdk.jpackage.internal.Arguments.CLIOptions;
+
+/**
+ * ValidOptions
+ *
+ * Two basic methods for validating command line options.
+ *
+ * initArgs()
+ *      Computes the Map of valid options for each mode on this Platform.
+ *
+ * checkIfSupported(CLIOptions mode, CLIOptions arg)
+ *      Determine if the given arg is valid in the given mode.
+ *
+ * checkIfOtherSupported(CLIOptions mode, CLIOptions arg)
+ *      Determine if the given arg is valid in the a different mode.
+ */
+class ValidOptions {
+
+    private ValidOptions() {};
+
+    // multimap that contains pairs of (mode, supported args)
+    private static final Map<CLIOptions, Set<CLIOptions>> options =
+            new HashMap<>();
+
+    private static boolean argsInitialized = false;
+
+    // initializing list of mandatory arguments
+    private static void initArgs() {
+        if (argsInitialized) {
+            return;
+        }
+
+        // add options for CREATE_IMAGE
+        add(CLIOptions.CREATE_IMAGE, CLIOptions.INPUT);
+        add(CLIOptions.CREATE_IMAGE, CLIOptions.OUTPUT);
+        add(CLIOptions.CREATE_IMAGE, CLIOptions.APPCLASS);
+        add(CLIOptions.CREATE_IMAGE, CLIOptions.NAME);
+        add(CLIOptions.CREATE_IMAGE, CLIOptions.IDENTIFIER);
+        add(CLIOptions.CREATE_IMAGE, CLIOptions.VERBOSE);
+        add(CLIOptions.CREATE_IMAGE, CLIOptions.OVERWRITE);
+        add(CLIOptions.CREATE_IMAGE, CLIOptions.FILES);
+        add(CLIOptions.CREATE_IMAGE, CLIOptions.ARGUMENTS);
+        add(CLIOptions.CREATE_IMAGE, CLIOptions.STRIP_NATIVE_COMMANDS);
+        add(CLIOptions.CREATE_IMAGE, CLIOptions.ICON);
+        add(CLIOptions.CREATE_IMAGE, CLIOptions.VERSION);
+        add(CLIOptions.CREATE_IMAGE, CLIOptions.JVM_ARGS);
+        add(CLIOptions.CREATE_IMAGE, CLIOptions.SECONDARY_LAUNCHER);
+        add(CLIOptions.CREATE_IMAGE, CLIOptions.BUILD_ROOT);
+        add(CLIOptions.CREATE_IMAGE, CLIOptions.PREDEFINED_RUNTIME_IMAGE);
+        add(CLIOptions.CREATE_IMAGE, CLIOptions.MAIN_JAR);
+        add(CLIOptions.CREATE_IMAGE, CLIOptions.MODULE);
+        add(CLIOptions.CREATE_IMAGE, CLIOptions.ADD_MODULES);
+        add(CLIOptions.CREATE_IMAGE, CLIOptions.MODULE_PATH);
+        add(CLIOptions.CREATE_IMAGE, CLIOptions.RESOURCE_DIR);
+
+        if (Platform.getPlatform() == Platform.MAC) {
+            add(CLIOptions.CREATE_IMAGE, CLIOptions.MAC_SIGN);
+            add(CLIOptions.CREATE_IMAGE, CLIOptions.MAC_BUNDLE_NAME);
+            add(CLIOptions.CREATE_IMAGE, CLIOptions.MAC_BUNDLE_IDENTIFIER);
+            add(CLIOptions.CREATE_IMAGE, CLIOptions.MAC_BUNDLE_SIGNING_PREFIX);
+            add(CLIOptions.CREATE_IMAGE, CLIOptions.MAC_SIGNING_KEY_NAME);
+            add(CLIOptions.CREATE_IMAGE, CLIOptions.MAC_SIGNING_KEYCHAIN);
+            add(CLIOptions.CREATE_IMAGE, CLIOptions.CATEGORY);
+            add(CLIOptions.CREATE_IMAGE, CLIOptions.COPYRIGHT);
+        }
+
+        if (Platform.getPlatform() == Platform.WINDOWS) {
+            add(CLIOptions.CREATE_IMAGE, CLIOptions.DESCRIPTION);
+            add(CLIOptions.CREATE_IMAGE, CLIOptions.VENDOR);
+            add(CLIOptions.CREATE_IMAGE, CLIOptions.COPYRIGHT);
+            add(CLIOptions.CREATE_IMAGE, CLIOptions.WIN_CONSOLE_HINT);
+        }
+
+        // add options for CREATE_INSTALLER
+        // (start with all options for CREATE_IMAGE)
+        Set<CLIOptions> imageOptions = options.get(CLIOptions.CREATE_IMAGE);
+        imageOptions.forEach(o -> add(CLIOptions.CREATE_INSTALLER, o));
+
+        add(CLIOptions.CREATE_INSTALLER, CLIOptions.RUNTIME_INSTALLER);
+        add(CLIOptions.CREATE_INSTALLER, CLIOptions.INSTALLER_TYPE);
+        add(CLIOptions.CREATE_INSTALLER, CLIOptions.LICENSE_FILE);
+        add(CLIOptions.CREATE_INSTALLER, CLIOptions.FILE_ASSOCIATIONS);
+        add(CLIOptions.CREATE_INSTALLER, CLIOptions.INSTALL_DIR);
+        add(CLIOptions.CREATE_INSTALLER, CLIOptions.PREDEFINED_APP_IMAGE);
+        add(CLIOptions.CREATE_INSTALLER, CLIOptions.INSTALLER_TYPE);
+
+        if (Platform.getPlatform() == Platform.MAC) {
+            add(CLIOptions.CREATE_INSTALLER, CLIOptions.MAC_APP_STORE_CATEGORY);
+            add(CLIOptions.CREATE_INSTALLER,
+                    CLIOptions.MAC_APP_STORE_ENTITLEMENTS);
+        }
+
+        if (Platform.getPlatform() == Platform.LINUX) {
+            add(CLIOptions.CREATE_INSTALLER, CLIOptions.LINUX_BUNDLE_NAME);
+            add(CLIOptions.CREATE_INSTALLER, CLIOptions.LINUX_DEB_MAINTAINER);
+            add(CLIOptions.CREATE_INSTALLER, CLIOptions.LINUX_RPM_LICENSE_TYPE);
+            add(CLIOptions.CREATE_INSTALLER,
+                    CLIOptions.LINUX_PACKAGE_DEPENDENCIES);
+            add(CLIOptions.CREATE_INSTALLER, CLIOptions.DESCRIPTION);
+            add(CLIOptions.CREATE_INSTALLER, CLIOptions.VENDOR);
+            add(CLIOptions.CREATE_INSTALLER, CLIOptions.CATEGORY);
+            add(CLIOptions.CREATE_INSTALLER, CLIOptions.COPYRIGHT);
+        }
+
+        if (Platform.getPlatform() == Platform.WINDOWS) {
+            add(CLIOptions.CREATE_INSTALLER, CLIOptions.WIN_MENU_HINT);
+            add(CLIOptions.CREATE_INSTALLER, CLIOptions.WIN_MENU_GROUP);
+            add(CLIOptions.CREATE_INSTALLER, CLIOptions.WIN_SHORTCUT_HINT);
+            add(CLIOptions.CREATE_INSTALLER,
+                    CLIOptions.WIN_PER_USER_INSTALLATION);
+            add(CLIOptions.CREATE_INSTALLER, CLIOptions.WIN_DIR_CHOOSER);
+            add(CLIOptions.CREATE_INSTALLER, CLIOptions.WIN_REGISTRY_NAME);
+            add(CLIOptions.CREATE_INSTALLER, CLIOptions.WIN_UPGRADE_UUID);
+            add(CLIOptions.CREATE_INSTALLER, CLIOptions.CATEGORY);
+            add(CLIOptions.CREATE_INSTALLER, CLIOptions.WIN_CONSOLE_HINT);
+        }
+
+        argsInitialized = true;
+    }
+
+    static void add(CLIOptions mode, CLIOptions arg) {
+        if (mode.equals(arg)) {
+            return;
+        }
+        options.computeIfAbsent(mode,
+                    k -> new HashSet<>()).add(arg);
+    }
+
+    static boolean checkIfSupported(CLIOptions mode, CLIOptions arg) {
+        if (mode.equals(arg)) {
+            return true;
+        }
+
+        initArgs();
+        Set<CLIOptions> set = options.get(mode);
+        if (set != null) {
+            return set.contains(arg);
+        }
+        return false;
+    }
+
+    static boolean checkIfOtherSupported(CLIOptions mode, CLIOptions arg) {
+        for (CLIOptions other : options.keySet()) {
+            if (!other.equals(mode)) {
+                if (checkIfSupported(other, arg)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/VersionExtractor.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jpackage.internal;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.text.MessageFormat;
+import java.util.ResourceBundle;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class VersionExtractor extends PrintStream {
+
+    private static final ResourceBundle I18N = ResourceBundle.getBundle(
+            "jdk.jpackage.internal.resources.MainResources");
+
+    private final String pattern;
+    private String version = null;
+
+    public VersionExtractor(String pattern) {
+        super(new ByteArrayOutputStream());
+
+        this.pattern = pattern;
+    }
+
+    public String getVersion() {
+        if (version == null) {
+            String content
+                    = new String(((ByteArrayOutputStream) out).toByteArray());
+            Pattern p = Pattern.compile(pattern);
+            Matcher matcher = p.matcher(content);
+            if (matcher.find()) {
+                version = matcher.group(1);
+            }
+        }
+        return version;
+    }
+
+    public static boolean isLessThan(String version, String compareTo)
+            throws RuntimeException {
+        if (version == null || version.isEmpty()) {
+            throw new RuntimeException(MessageFormat.format(
+                    I18N.getString("ERR_VersionComparison"),
+                    version, compareTo));
+        }
+
+        if (compareTo == null || compareTo.isEmpty()) {
+            throw new RuntimeException(MessageFormat.format(
+                    I18N.getString("ERR_VersionComparison"),
+                    version, compareTo));
+        }
+
+        String [] versionArray = version.trim().split(Pattern.quote("."));
+        String [] compareToArray = compareTo.trim().split(Pattern.quote("."));
+
+        for (int i = 0; i < versionArray.length; i++) {
+            int v1 = Integer.parseInt(versionArray[i]);
+            int v2 = Integer.parseInt(compareToArray[i]);
+            if (v1 < v2) {
+                return true;
+            } else if (v1 > v2) {
+                return false;
+            }
+        }
+
+        return false;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/HelpResources.properties	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,237 @@
+#
+# Copyright (c) 2017, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+# particular file as subject to the "Classpath" exception as provided
+# by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+# or visit www.oracle.com if you need additional information or have any
+# questions.
+#
+#
+
+MSG_Help_common=Usage: jpackage <mode> <options>\n\
+\n\
+where mode is one of: \n\
+\  create-image\n\
+\          Generates a platform-specific application image.\n\
+\  create-installer\n\
+\          Generates a platform-specific installer for the application.\n\
+\          \n\
+Sample usages:\n\
+--------------\n\
+\    Generate a modular application image:\n\
+\        jpackage create-image --output outputdir --name AppName \\\n\
+\            --main-class package.ClassName --module moduleName -p modulePath\n\
+\        jpackage create-image --o outputdir -n AppName \\\n\
+\            -c package.ClassName -m moduleName -p modulePath\n\
+\    Generate a non-modular application image:\n\
+\        jpackage create-image --input inputdir --output outputdir \\\n\
+\            --name AppName --main-class package.ClassName --main-jar MyJar.jar\n\
+\        jpackage create-image -i inputdir -o outputdir -n AppName \\\n\
+\            -c package.ClassName -j MyJar.jar\n\
+\    Generate an application installer:\n\
+\        jpackage create-installer -i inputdir -o outputdir \\\n\
+\            -n <installer-name> -c package.ClassName -j MyJar.jar\n\
+\        jpackage create-installer -o outputdir -n <installer-name>\\\n\
+\            --app-image <app image dir>\n\
+\    Generate a Java runtime installer:\n\
+\        jpackage create-installer --runtime-installer\\\n\
+\            --name <installer-name> --output outputdir\n\
+\        jpackage create-installer --runtime-installer \\\n\
+\            -n <installer-name> -o outputdir --runtime-image <runtime-path>\n\
+\n\
+The following options are valid for all platforms:\n\
+\  @<filename> \n\
+\          Read options and/or mode from a file \n\
+\  --help -h \n\
+\          Print the usage text with a list and description of each valid\n\
+\          option for the current platform to the output stream, and exit\n\
+\  --version -v\n\
+\          Print the product version to the output stream and exit\n\
+\  --output -o <output path>\n\
+\          Path where generated output file is placed\n\
+\          (absolute path or relative to the current directory)\n\
+\  --input -i <input path>\n\
+\          Path of the input directory that contains the files to package\n\
+\          (absolute path or relative to the current directory)\n\
+\  --files -f <input file>[{0}<input file>...]\n\
+\          A {0} separated list of files in the input dir to be packaged\n\
+\          If omitted, all files in the input directory will be packaged.\n\
+\  --name -n <application name>\n\
+\          Name of the application\n\
+\  --main-jar -j <main jar file>\n\
+\          The main JAR of the application; containing the main class\n\
+\          (specified as a path relative to the input path)\n\
+\  --main-class -c <class name>\n\
+\          Qualified name of the application main class to execute\n\
+\  --installer-type <type> \n\
+\          The type of the installer created in create-image mode\n\
+\          Valid values for "type" are "msi", "exe", "rpm", "deb", "dmg",\n\
+\                           "pkg", and "pkg-app-store".\n\
+\          If this option is not specified (in create-installer mode),\n\
+\          all supported types of installable packages for the current\n\
+\          platform will be generated.\n\
+\  --runtime-installer \n\
+\          Generates a platform-specific installer for a Java runtime.\n\
+\  --app-version <version string>\n\
+\          Version of the application\n\
+\  --arguments -a <main class arguments>\n\
+\          Command line arguments to pass to the main class if no command\n\
+\          line arguments are given to the launcher\n\
+\  --icon <icon file path>\n\
+\          Path of the icon of the application bundle\n\
+\          (absolute path or relative to the current directory)\n\
+\  --identifier <id string>\n\
+\          Machine readable identifier of the application\n\
+\          The format must be a DNS name in reverse order,\n\
+\          such as com.example.myapplication.\n\
+\  --verbose\n\
+\          Enables verbose output\n\
+\  --strip-native-commands\n\
+\          Removes native executables from the custom run-time images\n\
+\  --jvm-args <java vm arguments>\n\
+\          Java arguments to pass to the virtual machine\n\
+\  --file-associations <file path>\n\
+\          Path to a Properties file that contains list of key, value pairs\n\
+\          (absolute path or relative to the current directory)\n\
+\          The keys "extension", "mime-type", "icon", and "description"\n\
+\          can be used to describe the association.\n\
+\  --secondary-launcher <file path>\n\
+\          Path to a Properties file that contains list of key, value pairs\n\
+\          (absolute path or relative to the current directory)\n\
+\          The keys "name" (required), "version", "module", "class",\n\
+\          "icon", "arguments", "jvm-args", "win-menu",\n\
+\          "win-shortcut", and "win-console" can be used.\n\
+\          These options are added to, or used to overwrite, the original\n\
+\          command line options when building the secondary launcher.\n\
+\  --build-root <file path>\n\
+\          Path of a new or empty directory used to create temporary files\n\
+\          (absolute path or relative to the current directory)\n\
+\          If not specified, a temporary directory will be created and\n\
+\          removed upon the task completion.\n\
+\  --runtime-image <file path>\n\
+\          Path of the predefined runtime image that is used to build\n\
+\          an application image and installable package\n\
+\          (absolute path or relative to the current directory)\n\
+\  --app-image <file path>\n\
+\          Location of the predefined application image that is used\n\
+\          to build an installable package\n\
+\          (absolute path or relative to the current directory)\n\
+\  --install-dir <file path>\n\
+\          Absolute path of the installation directory of the application\n\
+\          This option is ignored on Windows, use --win-dir-chooser to\n\
+\          provide user the ability to choose the installation directory.\n\
+\  --license-file <file path>\n\
+\          Path to the license file\n\
+\          (absolute path or relative to the current directory)\n\
+\  --copyright <copyright string>\n\
+\          Copyright for the application\n\
+\  --description <description string>\n\
+\          Description of the application\n\
+\  --category <category string>\n\
+\          Category or group of the application\n\
+\  --vendor <vendor string>\n\
+\          Vendor of the application\n\
+\  --overwrite\n\
+\          Allow the recursive deletion of an existing application \n\
+\          output directory when creating an application image\n\
+\  --resource-dir <path>\n\
+\          Path to override jpackage resources\n\
+\          Icons, template files, and other resources of jpackage can be\n\
+\          over-ridden by adding replacement resources to this directory.\n\
+\          (absolute path or relative to the current directory)\n\
+\n\
+Modular options:\n\
+\  --module -m <module name>\n\
+\          Main module of the application\n\
+\          This module must contain the main-class,\n\
+\          and be located on the module path.\n\
+\  --module-path -p <module path>\n\
+\          Path jlink looks in for modules when packaging the Java Runtime\n\
+\          (absolute path or relative to the current directory)\n\
+\  --add-modules <module name>[,<module name>...]\n\
+\          A comma (",") separated list of modules to add.\n\
+
+MSG_Help_mac=\nThe following options are valid for Mac OS X platforms:\n\
+\  --mac-sign\n\
+\          Request that the bundle be signed\n\
+\  --mac-bundle-name <name string>\n\
+\          Name of the application as it appears in the Menu Bar\n\
+\          This can be different from the application name.\n\
+\          This name must be less than 16 characters long and be suitable for\n\
+\          displaying in the menu bar and the application Info window.\n\
+\          Defaults to the application name.\n\
+\  --mac-bundle-identifier <ID string>\n\
+\          An identifier that uniquely identifies the application for MacOSX\n\
+\          (and on the Mac App Store)\n\
+\          May only use alphanumeric (A-Z,a-z,0-9), hyphen (-),\n\
+\          and period (.) characters.\n\
+\  --mac-app-store-category <category string>\n\
+\          Mac App Store Categories\n\
+\          Note that the key is the string shown to\n\
+\          the user and the value is the ID of the category.\n\
+\  --mac-app-store-entitlements <file path>\n\
+\          Path of a custom mac app store entitlements file\n\
+\          (absolute path or relative to the current directory)\n\
+\  --mac-bundle-signing-prefix <prefix string>\n\
+\          When signing the application bundle, this value is prefixed to all\n\
+\          components that need to be signed that don't have\n\
+\          an existing bundle identifier.\n\
+\  --mac-signing-key-user-name <user name>\n\
+\          User name portion of the typical\n\
+\          "Mac Developer ID Application: <user name>" signing key\n\
+\  --mac-signing-keychain <file path>\n\
+\          Path of the keychain to use\n\
+\          (absolute path or relative to the current directory)\n\
+\          If not specified, the standard keychains are used.\n\
+
+MSG_Help_linux=\nThe following options are valid for Linux platforms:\n\
+\  --linux-bundle-name <bundle name>\n\
+\          Name for Linux bundle, defaults to the application name\n\
+\  --linux-package-deps\n\
+\          Required packages or capabilities for the application\n\
+\  --linux-rpm-license-type <type string>\n\
+\          Type of the license ("License: <value>" of the RPM .spec)\n\
+\  --linux-deb-maintainer <email address>\n\
+\          Maintainer for .deb bundle\n\
+
+MSG_Help_win=\nThe following options are valid for Windows platforms:\n\
+\  --win-menu\n\
+\          Adds the application to the system menu\n\
+\  --win-menu-group <menu group name>\n\
+\          Start Menu group this application is placed in\n\
+\  --win-per-user-install\n\
+\          Request to perform an install on a per-user basis\n\
+\  --win-dir-chooser\n\
+\          Adds a dialog to enable the user to choose a directory in which\n\
+\          the product is installed\n\
+\  --win-registry-name <registry name>\n\
+\          Name of the application for registry references.\n\
+\          The default is the Application Name with only\n\
+\          alphanumerics, dots, and dashes (no whitespace)\n\
+\  --win-upgrade-uuid <id string>\n\
+\          UUID associated with upgrades for this package\n\
+\  --win-shortcut\n\
+\          Creates a desktop shortcut for the application\n\
+\  --win-console\n\
+\          Creates a console launcher for the application, should be\n\
+\          specified for application which requires console interactions\n\
+
+MSG_Help_no_args=Usage: jpackage <mode> <options>\n\
+\Use jpackage --help (or -h) for a list of possible options\
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/HelpResources_ja.properties	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,237 @@
+#
+# Copyright (c) 2017, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+# particular file as subject to the "Classpath" exception as provided
+# by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+# or visit www.oracle.com if you need additional information or have any
+# questions.
+#
+#
+
+MSG_Help_common=Usage: jpackage <mode> <options>\n\
+\n\
+where mode is one of: \n\
+\  create-image\n\
+\          Generates a platform-specific application image.\n\
+\  create-installer\n\
+\          Generates a platform-specific installer for the application.\n\
+\          \n\
+Sample usages:\n\
+--------------\n\
+\    Generate a modular application image:\n\
+\        jpackage create-image --output outputdir --name AppName \\\n\
+\            --main-class package.ClassName --module moduleName -p modulePath\n\
+\        jpackage create-image --o outputdir -n AppName \\\n\
+\            -c package.ClassName -m moduleName -p modulePath\n\
+\    Generate a non-modular application image:\n\
+\        jpackage create-image --input inputdir --output outputdir \\\n\
+\            --name AppName --main-class package.ClassName --main-jar MyJar.jar\n\
+\        jpackage create-image -i inputdir -o outputdir -n AppName \\\n\
+\            -c package.ClassName -j MyJar.jar\n\
+\    Generate an application installer:\n\
+\        jpackage create-installer -i inputdir -o outputdir \\\n\
+\            -n <installer-name> -c package.ClassName -j MyJar.jar\n\
+\        jpackage create-installer -o outputdir -n <installer-name>\\\n\
+\            --app-image <app image dir>\n\
+\    Generate a Java runtime installer:\n\
+\        jpackage create-installer --runtime-installer\\\n\
+\            --name <installer-name> --output outputdir\n\
+\        jpackage create-installer --runtime-installer \\\n\
+\            -n <installer-name> -o outputdir --runtime-image <runtime-path>\n\
+\n\
+The following options are valid for all platforms:\n\
+\  @<filename> \n\
+\          Read options and/or mode from a file \n\
+\  --help -h \n\
+\          Print the usage text with a list and description of each valid\n\
+\          option for the current platform to the output stream, and exit\n\
+\  --version -v\n\
+\          Print the product version to the output stream and exit\n\
+\  --output -o <output path>\n\
+\          Path where generated output file is placed\n\
+\          (absolute path or relative to the current directory)\n\
+\  --input -i <input path>\n\
+\          Path of the input directory that contains the files to package\n\
+\          (absolute path or relative to the current directory)\n\
+\  --files -f <input file>[{0}<input file>...]\n\
+\          A {0} separated list of files in the input dir to be packaged\n\
+\          If omitted, all files in the input directory will be packaged.\n\
+\  --name -n <application name>\n\
+\          Name of the application\n\
+\  --main-jar -j <main jar file>\n\
+\          The main JAR of the application; containing the main class\n\
+\          (specified as a path relative to the input path)\n\
+\  --main-class -c <class name>\n\
+\          Qualified name of the application main class to execute\n\
+\  --installer-type <type> \n\
+\          The type of the installer created in create-image mode\n\
+\          Valid values for "type" are "msi", "exe", "rpm", "deb", "dmg",\n\
+\                           "pkg", and "pkg-app-store".\n\
+\          If this option is not specified (in create-installer mode),\n\
+\          all supported types of installable packages for the current\n\
+\          platform will be generated.\n\
+\  --runtime-installer \n\
+\          Generates a platform-specific installer for a Java runtime.\n\
+\  --app-version <version string>\n\
+\          Version of the application\n\
+\  --arguments -a <main class arguments>\n\
+\          Command line arguments to pass to the main class if no command\n\
+\          line arguments are given to the launcher\n\
+\  --icon <icon file path>\n\
+\          Path of the icon of the application bundle\n\
+\          (absolute path or relative to the current directory)\n\
+\  --identifier <id string>\n\
+\          Machine readable identifier of the application\n\
+\          The format must be a DNS name in reverse order,\n\
+\          such as com.example.myapplication.\n\
+\  --verbose\n\
+\          Enables verbose output\n\
+\  --strip-native-commands\n\
+\          Removes native executables from the custom run-time images\n\
+\  --jvm-args <java vm arguments>\n\
+\          Java arguments to pass to the virtual machine\n\
+\  --file-associations <file path>\n\
+\          Path to a Properties file that contains list of key, value pairs\n\
+\          (absolute path or relative to the current directory)\n\
+\          The keys "extension", "mime-type", "icon", and "description"\n\
+\          can be used to describe the association.\n\
+\  --secondary-launcher <file path>\n\
+\          Path to a Properties file that contains list of key, value pairs\n\
+\          (absolute path or relative to the current directory)\n\
+\          The keys "name" (required), "version", "module", "class",\n\
+\          "icon", "arguments", "jvm-args", "win-menu",\n\
+\          "win-shortcut", and "win-console" can be used.\n\
+\          These options are added to, or used to overwrite, the original\n\
+\          command line options when building the secondary launcher.\n\
+\  --build-root <file path>\n\
+\          Path of a new or empty directory used to create temporary files\n\
+\          (absolute path or relative to the current directory)\n\
+\          If not specified, a temporary directory will be created and\n\
+\          removed upon the task completion.\n\
+\  --runtime-image <file path>\n\
+\          Path of the predefined runtime image that is used to build\n\
+\          an application image and installable package\n\
+\          (absolute path or relative to the current directory)\n\
+\  --app-image <file path>\n\
+\          Location of the predefined application image that is used\n\
+\          to build an installable package\n\
+\          (absolute path or relative to the current directory)\n\
+\  --install-dir <file path>\n\
+\          Absolute path of the installation directory of the application\n\
+\          This option is ignored on Windows, use --win-dir-chooser to\n\
+\          provide user the ability to choose the installation directory.\n\
+\  --license-file <file path>\n\
+\          Path to the license file\n\
+\          (absolute path or relative to the current directory)\n\
+\  --copyright <copyright string>\n\
+\          Copyright for the application\n\
+\  --description <description string>\n\
+\          Description of the application\n\
+\  --category <category string>\n\
+\          Category or group of the application\n\
+\  --vendor <vendor string>\n\
+\          Vendor of the application\n\
+\  --overwrite\n\
+\          Allow the recursive deletion of an existing application \n\
+\          output directory when creating an application image\n\
+\  --resource-dir <path>\n\
+\          Path to override jpackage resources\n\
+\          Icons, template files, and other resources of jpackage can be\n\
+\          over-ridden by adding replacement resources to this directory.\n\
+\          (absolute path or relative to the current directory)\n\
+\n\
+Modular options:\n\
+\  --module -m <module name>\n\
+\          Main module of the application\n\
+\          This module must contain the main-class,\n\
+\          and be located on the module path.\n\
+\  --module-path -p <module path>\n\
+\          Path jlink looks in for modules when packaging the Java Runtime\n\
+\          (absolute path or relative to the current directory)\n\
+\  --add-modules <module name>[,<module name>...]\n\
+\          A comma (",") separated list of modules to add.\n\
+
+MSG_Help_mac=\nThe following options are valid for Mac OS X platforms:\n\
+\  --mac-sign\n\
+\          Request that the bundle be signed\n\
+\  --mac-bundle-name <name string>\n\
+\          Name of the application as it appears in the Menu Bar\n\
+\          This can be different from the application name.\n\
+\          This name must be less than 16 characters long and be suitable for\n\
+\          displaying in the menu bar and the application Info window.\n\
+\          Defaults to the application name.\n\
+\  --mac-bundle-identifier <ID string>\n\
+\          An identifier that uniquely identifies the application for MacOSX\n\
+\          (and on the Mac App Store)\n\
+\          May only use alphanumeric (A-Z,a-z,0-9), hyphen (-),\n\
+\          and period (.) characters.\n\
+\  --mac-app-store-category <category string>\n\
+\          Mac App Store Categories\n\
+\          Note that the key is the string shown to\n\
+\          the user and the value is the ID of the category.\n\
+\  --mac-app-store-entitlements <file path>\n\
+\          Path of a custom mac app store entitlements file\n\
+\          (absolute path or relative to the current directory)\n\
+\  --mac-bundle-signing-prefix <prefix string>\n\
+\          When signing the application bundle, this value is prefixed to all\n\
+\          components that need to be signed that don't have\n\
+\          an existing bundle identifier.\n\
+\  --mac-signing-key-user-name <user name>\n\
+\          User name portion of the typical\n\
+\          "Mac Developer ID Application: <user name>" signing key\n\
+\  --mac-signing-keychain <file path>\n\
+\          Path of the keychain to use\n\
+\          (absolute path or relative to the current directory)\n\
+\          If not specified, the standard keychains are used.\n\
+
+MSG_Help_linux=\nThe following options are valid for Linux platforms:\n\
+\  --linux-bundle-name <bundle name>\n\
+\          Name for Linux bundle, defaults to the application name\n\
+\  --linux-package-deps\n\
+\          Required packages or capabilities for the application\n\
+\  --linux-rpm-license-type <type string>\n\
+\          Type of the license ("License: <value>" of the RPM .spec)\n\
+\  --linux-deb-maintainer <email address>\n\
+\          Maintainer for .deb bundle\n\
+
+MSG_Help_win=\nThe following options are valid for Windows platforms:\n\
+\  --win-menu\n\
+\          Adds the application to the system menu\n\
+\  --win-menu-group <menu group name>\n\
+\          Start Menu group this application is placed in\n\
+\  --win-per-user-install\n\
+\          Request to perform an install on a per-user basis\n\
+\  --win-dir-chooser\n\
+\          Adds a dialog to enable the user to choose a directory in which\n\
+\          the product is installed\n\
+\  --win-registry-name <registry name>\n\
+\          Name of the application for registry references.\n\
+\          The default is the Application Name with only\n\
+\          alphanumerics, dots, and dashes (no whitespace)\n\
+\  --win-upgrade-uuid <id string>\n\
+\          UUID associated with upgrades for this package\n\
+\  --win-shortcut\n\
+\          Creates a desktop shortcut for the application\n\
+\  --win-console\n\
+\          Creates a console launcher for the application, should be\n\
+\          specified for application which requires console interactions\n\
+
+MSG_Help_no_args=Usage: jpackage <mode> <options>\n\
+\Use jpackage --help (or -h) for a list of possible options\
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/HelpResources_zh_CN.properties	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,237 @@
+#
+# Copyright (c) 2017, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+# particular file as subject to the "Classpath" exception as provided
+# by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+# or visit www.oracle.com if you need additional information or have any
+# questions.
+#
+#
+
+MSG_Help_common=Usage: jpackage <mode> <options>\n\
+\n\
+where mode is one of: \n\
+\  create-image\n\
+\          Generates a platform-specific application image.\n\
+\  create-installer\n\
+\          Generates a platform-specific installer for the application.\n\
+\          \n\
+Sample usages:\n\
+--------------\n\
+\    Generate a modular application image:\n\
+\        jpackage create-image --output outputdir --name AppName \\\n\
+\            --main-class package.ClassName --module moduleName -p modulePath\n\
+\        jpackage create-image --o outputdir -n AppName \\\n\
+\            -c package.ClassName -m moduleName -p modulePath\n\
+\    Generate a non-modular application image:\n\
+\        jpackage create-image --input inputdir --output outputdir \\\n\
+\            --name AppName --main-class package.ClassName --main-jar MyJar.jar\n\
+\        jpackage create-image -i inputdir -o outputdir -n AppName \\\n\
+\            -c package.ClassName -j MyJar.jar\n\
+\    Generate an application installer:\n\
+\        jpackage create-installer -i inputdir -o outputdir \\\n\
+\            -n <installer-name> -c package.ClassName -j MyJar.jar\n\
+\        jpackage create-installer -o outputdir -n <installer-name>\\\n\
+\            --app-image <app image dir>\n\
+\    Generate a Java runtime installer:\n\
+\        jpackage create-installer --runtime-installer\\\n\
+\            --name <installer-name> --output outputdir\n\
+\        jpackage create-installer --runtime-installer \\\n\
+\            -n <installer-name> -o outputdir --runtime-image <runtime-path>\n\
+\n\
+The following options are valid for all platforms:\n\
+\  @<filename> \n\
+\          Read options and/or mode from a file \n\
+\  --help -h \n\
+\          Print the usage text with a list and description of each valid\n\
+\          option for the current platform to the output stream, and exit\n\
+\  --version -v\n\
+\          Print the product version to the output stream and exit\n\
+\  --output -o <output path>\n\
+\          Path where generated output file is placed\n\
+\          (absolute path or relative to the current directory)\n\
+\  --input -i <input path>\n\
+\          Path of the input directory that contains the files to package\n\
+\          (absolute path or relative to the current directory)\n\
+\  --files -f <input file>[{0}<input file>...]\n\
+\          A {0} separated list of files in the input dir to be packaged\n\
+\          If omitted, all files in the input directory will be packaged.\n\
+\  --name -n <application name>\n\
+\          Name of the application\n\
+\  --main-jar -j <main jar file>\n\
+\          The main JAR of the application; containing the main class\n\
+\          (specified as a path relative to the input path)\n\
+\  --main-class -c <class name>\n\
+\          Qualified name of the application main class to execute\n\
+\  --installer-type <type> \n\
+\          The type of the installer created in create-image mode\n\
+\          Valid values for "type" are "msi", "exe", "rpm", "deb", "dmg",\n\
+\                           "pkg", and "pkg-app-store".\n\
+\          If this option is not specified (in create-installer mode),\n\
+\          all supported types of installable packages for the current\n\
+\          platform will be generated.\n\
+\  --runtime-installer \n\
+\          Generates a platform-specific installer for a Java runtime.\n\
+\  --app-version <version string>\n\
+\          Version of the application\n\
+\  --arguments -a <main class arguments>\n\
+\          Command line arguments to pass to the main class if no command\n\
+\          line arguments are given to the launcher\n\
+\  --icon <icon file path>\n\
+\          Path of the icon of the application bundle\n\
+\          (absolute path or relative to the current directory)\n\
+\  --identifier <id string>\n\
+\          Machine readable identifier of the application\n\
+\          The format must be a DNS name in reverse order,\n\
+\          such as com.example.myapplication.\n\
+\  --verbose\n\
+\          Enables verbose output\n\
+\  --strip-native-commands\n\
+\          Removes native executables from the custom run-time images\n\
+\  --jvm-args <java vm arguments>\n\
+\          Java arguments to pass to the virtual machine\n\
+\  --file-associations <file path>\n\
+\          Path to a Properties file that contains list of key, value pairs\n\
+\          (absolute path or relative to the current directory)\n\
+\          The keys "extension", "mime-type", "icon", and "description"\n\
+\          can be used to describe the association.\n\
+\  --secondary-launcher <file path>\n\
+\          Path to a Properties file that contains list of key, value pairs\n\
+\          (absolute path or relative to the current directory)\n\
+\          The keys "name" (required), "version", "module", "class",\n\
+\          "icon", "arguments", "jvm-args", "win-menu",\n\
+\          "win-shortcut", and "win-console" can be used.\n\
+\          These options are added to, or used to overwrite, the original\n\
+\          command line options when building the secondary launcher.\n\
+\  --build-root <file path>\n\
+\          Path of a new or empty directory used to create temporary files\n\
+\          (absolute path or relative to the current directory)\n\
+\          If not specified, a temporary directory will be created and\n\
+\          removed upon the task completion.\n\
+\  --runtime-image <file path>\n\
+\          Path of the predefined runtime image that is used to build\n\
+\          an application image and installable package\n\
+\          (absolute path or relative to the current directory)\n\
+\  --app-image <file path>\n\
+\          Location of the predefined application image that is used\n\
+\          to build an installable package\n\
+\          (absolute path or relative to the current directory)\n\
+\  --install-dir <file path>\n\
+\          Absolute path of the installation directory of the application\n\
+\          This option is ignored on Windows, use --win-dir-chooser to\n\
+\          provide user the ability to choose the installation directory.\n\
+\  --license-file <file path>\n\
+\          Path to the license file\n\
+\          (absolute path or relative to the current directory)\n\
+\  --copyright <copyright string>\n\
+\          Copyright for the application\n\
+\  --description <description string>\n\
+\          Description of the application\n\
+\  --category <category string>\n\
+\          Category or group of the application\n\
+\  --vendor <vendor string>\n\
+\          Vendor of the application\n\
+\  --overwrite\n\
+\          Allow the recursive deletion of an existing application \n\
+\          output directory when creating an application image\n\
+\  --resource-dir <path>\n\
+\          Path to override jpackage resources\n\
+\          Icons, template files, and other resources of jpackage can be\n\
+\          over-ridden by adding replacement resources to this directory.\n\
+\          (absolute path or relative to the current directory)\n\
+\n\
+Modular options:\n\
+\  --module -m <module name>\n\
+\          Main module of the application\n\
+\          This module must contain the main-class,\n\
+\          and be located on the module path.\n\
+\  --module-path -p <module path>\n\
+\          Path jlink looks in for modules when packaging the Java Runtime\n\
+\          (absolute path or relative to the current directory)\n\
+\  --add-modules <module name>[,<module name>...]\n\
+\          A comma (",") separated list of modules to add.\n\
+
+MSG_Help_mac=\nThe following options are valid for Mac OS X platforms:\n\
+\  --mac-sign\n\
+\          Request that the bundle be signed\n\
+\  --mac-bundle-name <name string>\n\
+\          Name of the application as it appears in the Menu Bar\n\
+\          This can be different from the application name.\n\
+\          This name must be less than 16 characters long and be suitable for\n\
+\          displaying in the menu bar and the application Info window.\n\
+\          Defaults to the application name.\n\
+\  --mac-bundle-identifier <ID string>\n\
+\          An identifier that uniquely identifies the application for MacOSX\n\
+\          (and on the Mac App Store)\n\
+\          May only use alphanumeric (A-Z,a-z,0-9), hyphen (-),\n\
+\          and period (.) characters.\n\
+\  --mac-app-store-category <category string>\n\
+\          Mac App Store Categories\n\
+\          Note that the key is the string shown to\n\
+\          the user and the value is the ID of the category.\n\
+\  --mac-app-store-entitlements <file path>\n\
+\          Path of a custom mac app store entitlements file\n\
+\          (absolute path or relative to the current directory)\n\
+\  --mac-bundle-signing-prefix <prefix string>\n\
+\          When signing the application bundle, this value is prefixed to all\n\
+\          components that need to be signed that don't have\n\
+\          an existing bundle identifier.\n\
+\  --mac-signing-key-user-name <user name>\n\
+\          User name portion of the typical\n\
+\          "Mac Developer ID Application: <user name>" signing key\n\
+\  --mac-signing-keychain <file path>\n\
+\          Path of the keychain to use\n\
+\          (absolute path or relative to the current directory)\n\
+\          If not specified, the standard keychains are used.\n\
+
+MSG_Help_linux=\nThe following options are valid for Linux platforms:\n\
+\  --linux-bundle-name <bundle name>\n\
+\          Name for Linux bundle, defaults to the application name\n\
+\  --linux-package-deps\n\
+\          Required packages or capabilities for the application\n\
+\  --linux-rpm-license-type <type string>\n\
+\          Type of the license ("License: <value>" of the RPM .spec)\n\
+\  --linux-deb-maintainer <email address>\n\
+\          Maintainer for .deb bundle\n\
+
+MSG_Help_win=\nThe following options are valid for Windows platforms:\n\
+\  --win-menu\n\
+\          Adds the application to the system menu\n\
+\  --win-menu-group <menu group name>\n\
+\          Start Menu group this application is placed in\n\
+\  --win-per-user-install\n\
+\          Request to perform an install on a per-user basis\n\
+\  --win-dir-chooser\n\
+\          Adds a dialog to enable the user to choose a directory in which\n\
+\          the product is installed\n\
+\  --win-registry-name <registry name>\n\
+\          Name of the application for registry references.\n\
+\          The default is the Application Name with only\n\
+\          alphanumerics, dots, and dashes (no whitespace)\n\
+\  --win-upgrade-uuid <id string>\n\
+\          UUID associated with upgrades for this package\n\
+\  --win-shortcut\n\
+\          Creates a desktop shortcut for the application\n\
+\  --win-console\n\
+\          Creates a console launcher for the application, should be\n\
+\          specified for application which requires console interactions\n\
+
+MSG_Help_no_args=Usage: jpackage <mode> <options>\n\
+\Use jpackage --help (or -h) for a list of possible options\
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources.properties	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,179 @@
+#
+# Copyright (c) 2017, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+# particular file as subject to the "Classpath" exception as provided
+# by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+# or visit www.oracle.com if you need additional information or have any
+# questions.
+#
+#
+
+param.images-root.name=Image Root
+param.images-root.description=Image Root
+param.config-root.name=Config Root
+param.config-root.description=Config root dir
+param.create-image.name=Create Image
+param.create-image.description=Creates platform-specific application image.
+param.create-installer.name=Create Installer
+param.create-installer.description=Creates platform-specific installer for the application.
+param.create-jre-installer.name=Create JRE Installer
+param.create-jre-installer.description=Creates platform-specific JRE installer.
+param.app-name.name=App Name
+param.app-name.description=The name of the application.
+param.app-resource.description=All of the files to place in the resources directory.  Including all needed jars as assets.
+param.app-resources.name=Resources
+param.app-resource-list.description=A List of RelativeFileSet objects containing all of the files to place in the resources directory.  Including all needed jars as assets.
+param.app-resources-list.name=Resources List
+param.build-root.name=Build Root
+param.build-root.description=The directory in which to use and place temporary files.
+param.category.name=Category
+param.category.description=The category or group of the application.  Generally speaking you will also want to specify application specific categories as well.
+param.category.default=Unknown
+param.copyright.name=Copyright
+param.copyright.description=The copyright for the application.
+param.copyright.default=Copyright (C) {0,date,YYYY}
+param.description.name=Description
+param.description.description=A longer description of the application
+param.description.default=none
+param.icon-file.name=Icon
+param.icon-file.description=The main icon of the application bundle.
+param.identifier.name=Identifier
+param.identifier.description=What is the machine readable identifier of this application?  The format should be a DNS name in reverse order, such as com.example.myapplication.
+param.arguments.name=Command Line Arguments
+param.arguments.description=Command Line Arguments to be passed to the main class if no arguments are specified by the launcher.
+param.jvm-options.name=JVM Options
+param.jvm-options.description=JVM flags and options to be passed in.
+param.jvm-system-properties.name=JVM System Properties
+param.jvm-system-properties.description=JVM System Properties (of the -Dname\=value variety).
+param.license-file.name=License
+param.license-file.description=The license file, relative to the assembled application directory.
+param.main-class.name=Main Class
+param.main-class.description=The main class for the application.  Either a javafx.application.Application instance or a java class with a main method.
+param.main-module.name=Main Module
+param.main-module.description=The main module for the application.  This is the module containing the main class.
+param.classpath.name=Main Jar Classpath
+param.classpath.description=The classpath from the main jar of the application, relative to the assembled application directory.
+param.main-jar.name=Main Jar
+param.main-jar.description=The main jar of the application.  This jar should have the main-class, and is relative to the assembled application dir.
+param.name.name=Name
+param.name.description=The name of the application.
+param.preferences-id.name=Preferences ID
+param.preferences-id.description=The preferences node to search for User JVM Options.  The format be a slash delimited version of the main package name, such as "com/example/myapplication".
+param.title.name=Title
+param.title.description=A title for the application.
+param.vendor.name=Vendor
+param.vendor.description=The vendor of the application.
+param.vendor.default=Unknown
+param.predefined-app-image.name=Predefined Application Image
+param.predefined-app-image.description=Location of the predefined application image that is used to build an installable package.
+param.predefined-runtime-image.name=Predefined Runtime Image
+param.predefined-runtime-image.description=Location of the custom runtime image that is used to build an application image and installable packages.
+param.version.name=Version
+param.version.description=The version of this application.
+param.version.default=1.0
+param.verbose.name=Verbose
+param.verbose.description=Flag to print out more information and saves configuration files for bundlers.
+param.overwrite.name=Overwrite
+param.overwrite.description=Flag to allow removal of existing application output directory
+param.resource-dir.name=Resource Dir
+param.resource-dir.description=The directory to look for bundler specific resources.
+param.secondary-launchers.name=Secondary Launchers
+param.secondary-launchers.description=A collection of bundle param info for secondary launchers
+param.file-associations.name=File Associations
+param.file-associations.description=A list of maps where each map describes a file association.  Uses the "fileAssociation." series of bundle arguments in each map.
+param.fa-extension.name=File Association Extension
+param.fa-extension.description=The File Extension to be associated, just the extension no dots.
+param.fa-content-type.name=File Association Content Type
+param.fa-content-type.description=Content Type to be associated.  Such as application/x-vnd.my-awesome-app.
+param.fa-icon.name=File Association Icon
+param.fa-icon.description=The Icon to be used for associated files.  Defaults to the application icon.
+param.fa-description.name=File Association Description
+param.fa-description.description=The description to be used for associated files.  The default is "<appName> File".
+param.source-dir.name=Source Directory
+param.source-dir.description=Path to the directory containing the files to be bundled.
+param.module-path.name=Module Path
+param.module-path.description=When packaging the Java Runtime, this is the path jlink will look in for modules.
+param.add-modules.name=Add Modules
+param.add-modules.description=List of Modules to add to JImage creation, including possible services.
+param.limit-modules.name=Limit Modules
+param.limit-modules.description=Modules to Limit JImage creation to.
+param.strip-executables.name=Strip Native Executables
+param.strip-executables.description=Removes native executables from the JImage creation.
+param.main.module.name=Main Module
+param.main.module.description=The main module of the application.  This module should have the main-class, and is on the module path.
+param.install-dir.name=Installation Directory
+param.install-dir.description=Installation directory of the application.
+
+message.using-default-resource=Using default package resource {0} {1} (add {2} to the resource-dir to customize)
+message.no-default-resource=no default package resource {0} {1} (add {2} to the resource-dir to customize)
+message.using-custom-resource-from-file=Using custom package resource {0} (loaded from file {1})
+message.using-custom-resource=Using custom package resource {0} (loaded from {1})
+message.creating-app-bundle=Creating app bundle\: {0} in {1}
+message.detected.modules="Automatically adding detected modules: {0}."
+message.modules="Adding modules: {0} to runtime image."
+message.app-image-dir-does-not-exist=Specified application image directory {0}\: {1} does not exists
+message.app-image-dir-does-not-exist.advice=Confirm that the value for {0} exists
+message.runtime-image-dir-does-not-exist=Specified runtime image directory {0}\: {1} does not exists
+message.runtime-image-dir-does-not-exist.advice=Confirm that the value for {0} exists
+message.debug-working-directory=Kept working directory for debug\: {0}
+
+error.cannot-create-output-dir=Output directory {0} cannot be created.
+error.cannot-write-to-output-dir=Output directory {0} is not writable.
+error.root-exists-without-overwrite=Error: Application output directory {0} already exists and --overwrite is not specified
+error.no-application-class=Main application class is missing.
+error.no-application-class.advice=Please specify main application class.
+error.no-main-module=Main application module is missing.
+error.no-main-module.advice=Make sure to use fx\:module task to create modular application.
+error.no-main-class-with-main-jar=An application class was not specified nor was one found in the jar {0}
+error.no-main-class-with-main-jar.advice=Please specify a application class or ensure that the jar {0} specifies one in the manifest.
+error.no-main-class=An application class was not specified nor was one found in the supplied application resources
+error.no-main-class.advice=Please specify a application class or ensure that the appResources has a jar containing one in the manifest.
+error.main-jar-does-not-exist=The configured main jar does not exist {0}
+error.main-jar-does-not-exist.advice=The main jar must be specified relative to the app resources (not an absolute path), and must exist within those resources.
+
+warning.module.does.not.exist=Module [{0}] does not exist.
+warning.no.jdk.modules.found=Warning: No JDK Modules found.
+warning.unsupported.option=Warning: Option [{0}] is not supported on this platform.
+warning.unsupported.mode.option=Warning: Option [{0}] is not supported in {1} mode.
+warning.missing.arg.file=Warning: Missing argument file: {0}
+
+MSG_BundlerFailed=Error: Bundler "{1}" ({0}) failed to produce a bundle.
+MSG_BundlerPlatformException=Bundler {0} skipped because the bundler does not support bundling on this platform.
+MSG_BundlerConfigException=Bundler {0} skipped because of a configuration problem\: {1}  \n\
+Advice to fix\: {2}
+MSG_BundlerConfigExceptionNoAdvice=Bundler {0} skipped because of a configuration problem\: {1}
+MSG_BundlerRuntimeException=Bundler {0} failed because of {1}
+MSG_Version=jpackage version
+MSG_BundlerFailed=Error: Bundler "{1}" ({0}) failed to produce a bundle.
+
+
+ERR_MissingMode=Error: Mode is not specified
+ERR_MissingArgument=Error: Missing argument: {0}
+ERR_MissingAppResources=Error: No application jars found
+ERR_AppImageNotExist=Error: App image directory "{0}" does not exist
+ERR_AppImageInvalid=Error: App image directory "{0}" does not contain "app" sub-directory
+ERR_NoSecondaryLauncherName=Error: Secondary Launchers require a name parameter.
+ERR_NoUniqueName=Error: Secondary Launchers require a unique name parameter.
+ERR_NoJreInstallerName=Error: Jre Installers require a name parameter.
+ERR_InvalidAppName=Error: Invalid Application name: {0}.
+ERR_InvalidSLName=Error: Invalid Secondary Launcher name: {0}.
+ERR_LicenseFileNotExit=Error: Specified license file does not exist.
+ERR_BuildRootInvalid=Error: build-root ({0}) must be empty directory.
+ERR_InvalidOption=Error: Invalid Option: [{0}]
+ERR_VersionComparison=Error: Failed to compare version {0} with {1}.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_ja.properties	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,179 @@
+#
+# Copyright (c) 2017, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+# particular file as subject to the "Classpath" exception as provided
+# by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+# or visit www.oracle.com if you need additional information or have any
+# questions.
+#
+#
+
+param.images-root.name=Image Root
+param.images-root.description=Image Root
+param.config-root.name=Config Root
+param.config-root.description=Config root dir
+param.create-image.name=Create Image
+param.create-image.description=Creates platform-specific application image.
+param.create-installer.name=Create Installer
+param.create-installer.description=Creates platform-specific installer for the application.
+param.create-jre-installer.name=Create JRE Installer
+param.create-jre-installer.description=Creates platform-specific JRE installer.
+param.app-name.name=App Name
+param.app-name.description=The name of the application.
+param.app-resource.description=All of the files to place in the resources directory.  Including all needed jars as assets.
+param.app-resources.name=Resources
+param.app-resource-list.description=A List of RelativeFileSet objects containing all of the files to place in the resources directory.  Including all needed jars as assets.
+param.app-resources-list.name=Resources List
+param.build-root.name=Build Root
+param.build-root.description=The directory in which to use and place temporary files.
+param.category.name=Category
+param.category.description=The category or group of the application.  Generally speaking you will also want to specify application specific categories as well.
+param.category.default=Unknown
+param.copyright.name=Copyright
+param.copyright.description=The copyright for the application.
+param.copyright.default=Copyright (C) {0,date,YYYY}
+param.description.name=Description
+param.description.description=A longer description of the application
+param.description.default=none
+param.icon-file.name=Icon
+param.icon-file.description=The main icon of the application bundle.
+param.identifier.name=Identifier
+param.identifier.description=What is the machine readable identifier of this application?  The format should be a DNS name in reverse order, such as com.example.myapplication.
+param.arguments.name=Command Line Arguments
+param.arguments.description=Command Line Arguments to be passed to the main class if no arguments are specified by the launcher.
+param.jvm-options.name=JVM Options
+param.jvm-options.description=JVM flags and options to be passed in.
+param.jvm-system-properties.name=JVM System Properties
+param.jvm-system-properties.description=JVM System Properties (of the -Dname\=value variety).
+param.license-file.name=License
+param.license-file.description=The license file, relative to the assembled application directory.
+param.main-class.name=Main Class
+param.main-class.description=The main class for the application.  Either a javafx.application.Application instance or a java class with a main method.
+param.main-module.name=Main Module
+param.main-module.description=The main module for the application.  This is the module containing the main class.
+param.classpath.name=Main Jar Classpath
+param.classpath.description=The classpath from the main jar of the application, relative to the assembled application directory.
+param.main-jar.name=Main Jar
+param.main-jar.description=The main jar of the application.  This jar should have the main-class, and is relative to the assembled application dir.
+param.name.name=Name
+param.name.description=The name of the application.
+param.preferences-id.name=Preferences ID
+param.preferences-id.description=The preferences node to search for User JVM Options.  The format be a slash delimited version of the main package name, such as "com/example/myapplication".
+param.title.name=Title
+param.title.description=A title for the application.
+param.vendor.name=Vendor
+param.vendor.description=The vendor of the application.
+param.vendor.default=Unknown
+param.predefined-app-image.name=Predefined Application Image
+param.predefined-app-image.description=Location of the predefined application image that is used to build an installable package.
+param.predefined-runtime-image.name=Predefined Runtime Image
+param.predefined-runtime-image.description=Location of the custom runtime image that is used to build an application image and installable packages.
+param.version.name=Version
+param.version.description=The version of this application.
+param.version.default=1.0
+param.verbose.name=Verbose
+param.verbose.description=Flag to print out more information and saves configuration files for bundlers.
+param.overwrite.name=Overwrite
+param.overwrite.description=Flag to allow removal of existing application output directory
+param.resource-dir.name=Resource Dir
+param.resource-dir.description=The directory to look for bundler specific resources.
+param.secondary-launchers.name=Secondary Launchers
+param.secondary-launchers.description=A collection of bundle param info for secondary launchers
+param.file-associations.name=File Associations
+param.file-associations.description=A list of maps where each map describes a file association.  Uses the "fileAssociation." series of bundle arguments in each map.
+param.fa-extension.name=File Association Extension
+param.fa-extension.description=The File Extension to be associated, just the extension no dots.
+param.fa-content-type.name=File Association Content Type
+param.fa-content-type.description=Content Type to be associated.  Such as application/x-vnd.my-awesome-app.
+param.fa-icon.name=File Association Icon
+param.fa-icon.description=The Icon to be used for associated files.  Defaults to the application icon.
+param.fa-description.name=File Association Description
+param.fa-description.description=The description to be used for associated files.  The default is "<appName> File".
+param.source-dir.name=Source Directory
+param.source-dir.description=Path to the directory containing the files to be bundled.
+param.module-path.name=Module Path
+param.module-path.description=When packaging the Java Runtime, this is the path jlink will look in for modules.
+param.add-modules.name=Add Modules
+param.add-modules.description=List of Modules to add to JImage creation, including possible services.
+param.limit-modules.name=Limit Modules
+param.limit-modules.description=Modules to Limit JImage creation to.
+param.strip-executables.name=Strip Native Executables
+param.strip-executables.description=Removes native executables from the JImage creation.
+param.main.module.name=Main Module
+param.main.module.description=The main module of the application.  This module should have the main-class, and is on the module path.
+param.install-dir.name=Installation Directory
+param.install-dir.description=Installation directory of the application.
+
+message.using-default-resource=Using default package resource {0} {1} (add {2} to the resource-dir to customize)
+message.no-default-resource=no default package resource {0} {1} (add {2} to the resource-dir to customize)
+message.using-custom-resource-from-file=Using custom package resource {0} (loaded from file {1})
+message.using-custom-resource=Using custom package resource {0} (loaded from {1})
+message.creating-app-bundle=Creating app bundle\: {0} in {1}
+message.detected.modules="Automatically adding detected modules: {0}."
+message.modules="Adding modules: {0} to runtime image."
+message.app-image-dir-does-not-exist=Specified application image directory {0}\: {1} does not exists
+message.app-image-dir-does-not-exist.advice=Confirm that the value for {0} exists
+message.runtime-image-dir-does-not-exist=Specified runtime image directory {0}\: {1} does not exists
+message.runtime-image-dir-does-not-exist.advice=Confirm that the value for {0} exists
+message.debug-working-directory=Kept working directory for debug\: {0}
+
+error.cannot-create-output-dir=Output directory {0} cannot be created.
+error.cannot-write-to-output-dir=Output directory {0} is not writable.
+error.root-exists-without-overwrite=Error: Application output directory {0} already exists and --overwrite is not specified
+error.no-application-class=Main application class is missing.
+error.no-application-class.advice=Please specify main application class.
+error.no-main-module=Main application module is missing.
+error.no-main-module.advice=Make sure to use fx\:module task to create modular application.
+error.no-main-class-with-main-jar=An application class was not specified nor was one found in the jar {0}
+error.no-main-class-with-main-jar.advice=Please specify a application class or ensure that the jar {0} specifies one in the manifest.
+error.no-main-class=An application class was not specified nor was one found in the supplied application resources
+error.no-main-class.advice=Please specify a application class or ensure that the appResources has a jar containing one in the manifest.
+error.main-jar-does-not-exist=The configured main jar does not exist {0}
+error.main-jar-does-not-exist.advice=The main jar must be specified relative to the app resources (not an absolute path), and must exist within those resources.
+
+warning.module.does.not.exist=Module [{0}] does not exist.
+warning.no.jdk.modules.found=Warning: No JDK Modules found.
+warning.unsupported.option=Warning: Option [{0}] is not supported on this platform.
+warning.unsupported.mode.option=Warning: Option [{0}] is not supported in {1} mode.
+warning.missing.arg.file=Warning: Missing argument file: {0}
+
+MSG_BundlerFailed=Error: Bundler "{1}" ({0}) failed to produce a bundle.
+MSG_BundlerPlatformException=Bundler {0} skipped because the bundler does not support bundling on this platform.
+MSG_BundlerConfigException=Bundler {0} skipped because of a configuration problem\: {1}  \n\
+Advice to fix\: {2}
+MSG_BundlerConfigExceptionNoAdvice=Bundler {0} skipped because of a configuration problem\: {1}
+MSG_BundlerRuntimeException=Bundler {0} failed because of {1}
+MSG_Version=jpackage version
+MSG_BundlerFailed=Error: Bundler "{1}" ({0}) failed to produce a bundle.
+
+
+ERR_MissingMode=Error: Mode is not specified
+ERR_MissingArgument=Error: Missing argument: {0}
+ERR_MissingAppResources=Error: No application jars found
+ERR_AppImageNotExist=Error: App image directory "{0}" does not exist
+ERR_AppImageInvalid=Error: App image directory "{0}" does not contain "app" sub-directory
+ERR_NoSecondaryLauncherName=Error: Secondary Launchers require a name parameter.
+ERR_NoUniqueName=Error: Secondary Launchers require a unique name parameter.
+ERR_NoJreInstallerName=Error: Jre Installers require a name parameter.
+ERR_InvalidAppName=Error: Invalid Application name: {0}.
+ERR_InvalidSLName=Error: Invalid Secondary Launcher name: {0}.
+ERR_LicenseFileNotExit=Error: Specified license file does not exist.
+ERR_BuildRootInvalid=Error: build-root ({0}) must be empty directory.
+ERR_InvalidOption=Error: Invalid Option: [{0}]
+ERR_VersionComparison=Error: Failed to compare version {0} with {1}.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_zh_CN.properties	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,179 @@
+#
+# Copyright (c) 2017, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+# particular file as subject to the "Classpath" exception as provided
+# by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+# or visit www.oracle.com if you need additional information or have any
+# questions.
+#
+#
+
+param.images-root.name=Image Root
+param.images-root.description=Image Root
+param.config-root.name=Config Root
+param.config-root.description=Config root dir
+param.create-image.name=Create Image
+param.create-image.description=Creates platform-specific application image.
+param.create-installer.name=Create Installer
+param.create-installer.description=Creates platform-specific installer for the application.
+param.create-jre-installer.name=Create JRE Installer
+param.create-jre-installer.description=Creates platform-specific JRE installer.
+param.app-name.name=App Name
+param.app-name.description=The name of the application.
+param.app-resource.description=All of the files to place in the resources directory.  Including all needed jars as assets.
+param.app-resources.name=Resources
+param.app-resource-list.description=A List of RelativeFileSet objects containing all of the files to place in the resources directory.  Including all needed jars as assets.
+param.app-resources-list.name=Resources List
+param.build-root.name=Build Root
+param.build-root.description=The directory in which to use and place temporary files.
+param.category.name=Category
+param.category.description=The category or group of the application.  Generally speaking you will also want to specify application specific categories as well.
+param.category.default=Unknown
+param.copyright.name=Copyright
+param.copyright.description=The copyright for the application.
+param.copyright.default=Copyright (C) {0,date,YYYY}
+param.description.name=Description
+param.description.description=A longer description of the application
+param.description.default=none
+param.icon-file.name=Icon
+param.icon-file.description=The main icon of the application bundle.
+param.identifier.name=Identifier
+param.identifier.description=What is the machine readable identifier of this application?  The format should be a DNS name in reverse order, such as com.example.myapplication.
+param.arguments.name=Command Line Arguments
+param.arguments.description=Command Line Arguments to be passed to the main class if no arguments are specified by the launcher.
+param.jvm-options.name=JVM Options
+param.jvm-options.description=JVM flags and options to be passed in.
+param.jvm-system-properties.name=JVM System Properties
+param.jvm-system-properties.description=JVM System Properties (of the -Dname\=value variety).
+param.license-file.name=License
+param.license-file.description=The license file, relative to the assembled application directory.
+param.main-class.name=Main Class
+param.main-class.description=The main class for the application.  Either a javafx.application.Application instance or a java class with a main method.
+param.main-module.name=Main Module
+param.main-module.description=The main module for the application.  This is the module containing the main class.
+param.classpath.name=Main Jar Classpath
+param.classpath.description=The classpath from the main jar of the application, relative to the assembled application directory.
+param.main-jar.name=Main Jar
+param.main-jar.description=The main jar of the application.  This jar should have the main-class, and is relative to the assembled application dir.
+param.name.name=Name
+param.name.description=The name of the application.
+param.preferences-id.name=Preferences ID
+param.preferences-id.description=The preferences node to search for User JVM Options.  The format be a slash delimited version of the main package name, such as "com/example/myapplication".
+param.title.name=Title
+param.title.description=A title for the application.
+param.vendor.name=Vendor
+param.vendor.description=The vendor of the application.
+param.vendor.default=Unknown
+param.predefined-app-image.name=Predefined Application Image
+param.predefined-app-image.description=Location of the predefined application image that is used to build an installable package.
+param.predefined-runtime-image.name=Predefined Runtime Image
+param.predefined-runtime-image.description=Location of the custom runtime image that is used to build an application image and installable packages.
+param.version.name=Version
+param.version.description=The version of this application.
+param.version.default=1.0
+param.verbose.name=Verbose
+param.verbose.description=Flag to print out more information and saves configuration files for bundlers.
+param.overwrite.name=Overwrite
+param.overwrite.description=Flag to allow removal of existing application output directory
+param.resource-dir.name=Resource Dir
+param.resource-dir.description=The directory to look for bundler specific resources.
+param.secondary-launchers.name=Secondary Launchers
+param.secondary-launchers.description=A collection of bundle param info for secondary launchers
+param.file-associations.name=File Associations
+param.file-associations.description=A list of maps where each map describes a file association.  Uses the "fileAssociation." series of bundle arguments in each map.
+param.fa-extension.name=File Association Extension
+param.fa-extension.description=The File Extension to be associated, just the extension no dots.
+param.fa-content-type.name=File Association Content Type
+param.fa-content-type.description=Content Type to be associated.  Such as application/x-vnd.my-awesome-app.
+param.fa-icon.name=File Association Icon
+param.fa-icon.description=The Icon to be used for associated files.  Defaults to the application icon.
+param.fa-description.name=File Association Description
+param.fa-description.description=The description to be used for associated files.  The default is "<appName> File".
+param.source-dir.name=Source Directory
+param.source-dir.description=Path to the directory containing the files to be bundled.
+param.module-path.name=Module Path
+param.module-path.description=When packaging the Java Runtime, this is the path jlink will look in for modules.
+param.add-modules.name=Add Modules
+param.add-modules.description=List of Modules to add to JImage creation, including possible services.
+param.limit-modules.name=Limit Modules
+param.limit-modules.description=Modules to Limit JImage creation to.
+param.strip-executables.name=Strip Native Executables
+param.strip-executables.description=Removes native executables from the JImage creation.
+param.main.module.name=Main Module
+param.main.module.description=The main module of the application.  This module should have the main-class, and is on the module path.
+param.install-dir.name=Installation Directory
+param.install-dir.description=Installation directory of the application.
+
+message.using-default-resource=Using default package resource {0} {1} (add {2} to the resource-dir to customize)
+message.no-default-resource=no default package resource {0} {1} (add {2} to the resource-dir to customize)
+message.using-custom-resource-from-file=Using custom package resource {0} (loaded from file {1})
+message.using-custom-resource=Using custom package resource {0} (loaded from {1})
+message.creating-app-bundle=Creating app bundle\: {0} in {1}
+message.detected.modules="Automatically adding detected modules: {0}."
+message.modules="Adding modules: {0} to runtime image."
+message.app-image-dir-does-not-exist=Specified application image directory {0}\: {1} does not exists
+message.app-image-dir-does-not-exist.advice=Confirm that the value for {0} exists
+message.runtime-image-dir-does-not-exist=Specified runtime image directory {0}\: {1} does not exists
+message.runtime-image-dir-does-not-exist.advice=Confirm that the value for {0} exists
+message.debug-working-directory=Kept working directory for debug\: {0}
+
+error.cannot-create-output-dir=Output directory {0} cannot be created.
+error.cannot-write-to-output-dir=Output directory {0} is not writable.
+error.root-exists-without-overwrite=Error: Application output directory {0} already exists and --overwrite is not specified
+error.no-application-class=Main application class is missing.
+error.no-application-class.advice=Please specify main application class.
+error.no-main-module=Main application module is missing.
+error.no-main-module.advice=Make sure to use fx\:module task to create modular application.
+error.no-main-class-with-main-jar=An application class was not specified nor was one found in the jar {0}
+error.no-main-class-with-main-jar.advice=Please specify a application class or ensure that the jar {0} specifies one in the manifest.
+error.no-main-class=An application class was not specified nor was one found in the supplied application resources
+error.no-main-class.advice=Please specify a application class or ensure that the appResources has a jar containing one in the manifest.
+error.main-jar-does-not-exist=The configured main jar does not exist {0}
+error.main-jar-does-not-exist.advice=The main jar must be specified relative to the app resources (not an absolute path), and must exist within those resources.
+
+warning.module.does.not.exist=Module [{0}] does not exist.
+warning.no.jdk.modules.found=Warning: No JDK Modules found.
+warning.unsupported.option=Warning: Option [{0}] is not supported on this platform.
+warning.unsupported.mode.option=Warning: Option [{0}] is not supported in {1} mode.
+warning.missing.arg.file=Warning: Missing argument file: {0}
+
+MSG_BundlerFailed=Error: Bundler "{1}" ({0}) failed to produce a bundle.
+MSG_BundlerPlatformException=Bundler {0} skipped because the bundler does not support bundling on this platform.
+MSG_BundlerConfigException=Bundler {0} skipped because of a configuration problem\: {1}  \n\
+Advice to fix\: {2}
+MSG_BundlerConfigExceptionNoAdvice=Bundler {0} skipped because of a configuration problem\: {1}
+MSG_BundlerRuntimeException=Bundler {0} failed because of {1}
+MSG_Version=jpackage version
+MSG_BundlerFailed=Error: Bundler "{1}" ({0}) failed to produce a bundle.
+
+
+ERR_MissingMode=Error: Mode is not specified
+ERR_MissingArgument=Error: Missing argument: {0}
+ERR_MissingAppResources=Error: No application jars found
+ERR_AppImageNotExist=Error: App image directory "{0}" does not exist
+ERR_AppImageInvalid=Error: App image directory "{0}" does not contain "app" sub-directory
+ERR_NoSecondaryLauncherName=Error: Secondary Launchers require a name parameter.
+ERR_NoUniqueName=Error: Secondary Launchers require a unique name parameter.
+ERR_NoJreInstallerName=Error: Jre Installers require a name parameter.
+ERR_InvalidAppName=Error: Invalid Application name: {0}.
+ERR_InvalidSLName=Error: Invalid Secondary Launcher name: {0}.
+ERR_LicenseFileNotExit=Error: Specified license file does not exist.
+ERR_BuildRootInvalid=Error: build-root ({0}) must be empty directory.
+ERR_InvalidOption=Error: Invalid Option: [{0}]
+ERR_VersionComparison=Error: Failed to compare version {0} with {1}.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/ResourceLocator.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2011, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jpackage.internal.resources;
+
+public class ResourceLocator {
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/main/CommandLine.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,316 @@
+/*
+ * Copyright (c) 1999, 2018, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jpackage.main;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.nio.charset.Charset;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Various utility methods for processing Java tool command line arguments.
+ *
+ *  <p><b>This is NOT part of any supported API.
+ *  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>
+ */
+public class CommandLine {
+    /**
+     * Process Win32-style command files for the specified command line
+     * arguments and return the resulting arguments. A command file argument
+     * is of the form '@file' where 'file' is the name of the file whose
+     * contents are to be parsed for additional arguments. The contents of
+     * the command file are parsed using StreamTokenizer and the original
+     * '@file' argument replaced with the resulting tokens. Recursive command
+     * files are not supported. The '@' character itself can be quoted with
+     * the sequence '@@'.
+     * @param args the arguments that may contain @files
+     * @return the arguments, with @files expanded
+     * @throws IOException if there is a problem reading any of the @files
+     */
+    public static String[] parse(String[] args) throws IOException {
+        List<String> newArgs = new ArrayList<>();
+        appendParsedCommandArgs(newArgs, Arrays.asList(args));
+        return newArgs.toArray(new String[newArgs.size()]);
+    }
+
+    private static void appendParsedCommandArgs(List<String> newArgs, List<String> args) throws IOException {
+        for (String arg : args) {
+            if (arg.length() > 1 && arg.charAt(0) == '@') {
+                arg = arg.substring(1);
+                if (arg.charAt(0) == '@') {
+                    newArgs.add(arg);
+                } else {
+                    loadCmdFile(arg, newArgs);
+                }
+            } else {
+                newArgs.add(arg);
+            }
+        }
+    }
+
+    /**
+     * Process the given environment variable and appends any Win32-style
+     * command files for the specified command line arguments and return
+     * the resulting arguments. A command file argument
+     * is of the form '@file' where 'file' is the name of the file whose
+     * contents are to be parsed for additional arguments. The contents of
+     * the command file are parsed using StreamTokenizer and the original
+     * '@file' argument replaced with the resulting tokens. Recursive command
+     * files are not supported. The '@' character itself can be quoted with
+     * the sequence '@@'.
+     * @param envVariable the env variable to process
+     * @param args the arguments that may contain @files
+     * @return the arguments, with environment variable's content and expansion of @files
+     * @throws IOException if there is a problem reading any of the @files
+     * @throws com.sun.tools.javac.main.CommandLine.UnmatchedQuote
+     */
+    public static List<String> parse(String envVariable, List<String> args)
+            throws IOException, UnmatchedQuote {
+
+        List<String> inArgs = new ArrayList<>();
+        appendParsedEnvVariables(inArgs, envVariable);
+        inArgs.addAll(args);
+        List<String> newArgs = new ArrayList<>();
+        appendParsedCommandArgs(newArgs, inArgs);
+        return newArgs;
+    }
+
+    /**
+     * Process the given environment variable and appends any Win32-style
+     * command files for the specified command line arguments and return
+     * the resulting arguments. A command file argument
+     * is of the form '@file' where 'file' is the name of the file whose
+     * contents are to be parsed for additional arguments. The contents of
+     * the command file are parsed using StreamTokenizer and the original
+     * '@file' argument replaced with the resulting tokens. Recursive command
+     * files are not supported. The '@' character itself can be quoted with
+     * the sequence '@@'.
+     * @param envVariable the env variable to process
+     * @param args the arguments that may contain @files
+     * @return the arguments, with environment variable's content and expansion of @files
+     * @throws IOException if there is a problem reading any of the @files
+     * @throws com.sun.tools.javac.main.CommandLine.UnmatchedQuote
+     */
+    public static String[] parse(String envVariable, String[] args) throws IOException, UnmatchedQuote {
+        List<String> out = parse(envVariable, Arrays.asList(args));
+        return out.toArray(new String[out.size()]);
+    }
+
+    private static void loadCmdFile(String name, List<String> args) throws IOException {
+        try (Reader r = Files.newBufferedReader(Paths.get(name), Charset.defaultCharset())) {
+            Tokenizer t = new Tokenizer(r);
+            String s;
+            while ((s = t.nextToken()) != null) {
+                args.add(s);
+            }
+        }
+    }
+
+    public static class Tokenizer {
+        private final Reader in;
+        private int ch;
+
+        public Tokenizer(Reader in) throws IOException {
+            this.in = in;
+            ch = in.read();
+        }
+
+        public String nextToken() throws IOException {
+            skipWhite();
+            if (ch == -1) {
+                return null;
+            }
+
+            StringBuilder sb = new StringBuilder();
+            char quoteChar = 0;
+
+            while (ch != -1) {
+                switch (ch) {
+                    case ' ':
+                    case '\t':
+                    case '\f':
+                        if (quoteChar == 0) {
+                            return sb.toString();
+                        }
+                        sb.append((char) ch);
+                        break;
+
+                    case '\n':
+                    case '\r':
+                        return sb.toString();
+
+                    case '\'':
+                    case '"':
+                        if (quoteChar == 0) {
+                            quoteChar = (char) ch;
+                        } else if (quoteChar == ch) {
+                            quoteChar = 0;
+                        } else {
+                            sb.append((char) ch);
+                        }
+                        break;
+
+                    case '\\':
+                        if (quoteChar != 0) {
+                            ch = in.read();
+                            switch (ch) {
+                                case '\n':
+                                case '\r':
+                                    while (ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t' || ch == '\f') {
+                                        ch = in.read();
+                                    }
+                                    continue;
+
+                                case 'n':
+                                    ch = '\n';
+                                    break;
+                                case 'r':
+                                    ch = '\r';
+                                    break;
+                                case 't':
+                                    ch = '\t';
+                                    break;
+                                case 'f':
+                                    ch = '\f';
+                                    break;
+                            }
+                        }
+                        sb.append((char) ch);
+                        break;
+
+                    default:
+                        sb.append((char) ch);
+                }
+
+                ch = in.read();
+            }
+
+            return sb.toString();
+        }
+
+        void skipWhite() throws IOException {
+            while (ch != -1) {
+                switch (ch) {
+                    case ' ':
+                    case '\t':
+                    case '\n':
+                    case '\r':
+                    case '\f':
+                        break;
+
+                    case '#':
+                        ch = in.read();
+                        while (ch != '\n' && ch != '\r' && ch != -1) {
+                            ch = in.read();
+                        }
+                        break;
+
+                    default:
+                        return;
+                }
+
+                ch = in.read();
+            }
+        }
+    }
+
+    @SuppressWarnings("fallthrough")
+    private static void appendParsedEnvVariables(List<String> newArgs, String envVariable)
+            throws UnmatchedQuote {
+
+        if (envVariable == null) {
+            return;
+        }
+        String in = System.getenv(envVariable);
+        if (in == null || in.trim().isEmpty()) {
+            return;
+        }
+
+        final char NUL = (char)0;
+        final int len = in.length();
+
+        int pos = 0;
+        StringBuilder sb = new StringBuilder();
+        char quote = NUL;
+        char ch;
+
+        loop:
+        while (pos < len) {
+            ch = in.charAt(pos);
+            switch (ch) {
+                case '\"': case '\'':
+                    if (quote == NUL) {
+                        quote = ch;
+                    } else if (quote == ch) {
+                        quote = NUL;
+                    } else {
+                        sb.append(ch);
+                    }
+                    pos++;
+                    break;
+                case '\f': case '\n': case '\r': case '\t': case ' ':
+                    if (quote == NUL) {
+                        newArgs.add(sb.toString());
+                        sb.setLength(0);
+                        while (ch == '\f' || ch == '\n' || ch == '\r' || ch == '\t' || ch == ' ') {
+                            pos++;
+                            if (pos >= len) {
+                                break loop;
+                            }
+                            ch = in.charAt(pos);
+                        }
+                        break;
+                    }
+                    // fall through
+                default:
+                    sb.append(ch);
+                    pos++;
+            }
+        }
+        if (sb.length() != 0) {
+            newArgs.add(sb.toString());
+        }
+        if (quote != NUL) {
+            throw new UnmatchedQuote(envVariable);
+        }
+    }
+
+    public static class UnmatchedQuote extends Exception {
+        private static final long serialVersionUID = 0;
+
+        public final String variableName;
+
+        UnmatchedQuote(String variable) {
+            this.variableName = variable;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/main/Main.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2011, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jpackage.main;
+
+import jdk.jpackage.internal.Arguments;
+import jdk.jpackage.internal.Log;
+import jdk.jpackage.internal.CLIHelp;
+import java.io.PrintWriter;
+import java.util.ResourceBundle;
+
+public class Main {
+
+    private static final ResourceBundle bundle = ResourceBundle.getBundle(
+            "jdk.jpackage.internal.resources.MainResources");
+
+    private static final String version = bundle.getString("MSG_Version")
+            + " " + System.getProperty("java.version");
+
+    /**
+     * main(String... args)
+     * This is the entry point for the jpackage tool.
+     *
+     * @param args command line arguments
+     */
+    public static void main(String... args) throws Exception {
+        // Create logger with default system.out and system.err
+        Log.Logger logger = new Log.Logger(false);
+        Log.setLogger(logger);
+
+        int status = run(args);
+        System.exit(status);
+    }
+
+    /**
+     * run() - this is the entry point for the ToolProvider API.
+     *
+     * @param out output stream
+     * @param err error output stream
+     * @param args command line arguments
+     * @return an exit code. 0 means success, non-zero means an error occurred.
+     */
+    public static int run(PrintWriter out, PrintWriter err, String... args)
+            throws Exception {
+        // Create logger with provided streams
+        Log.Logger logger = new Log.Logger(false);
+        logger.setPrintWriter(out, err);
+        Log.setLogger(logger);
+
+        int status = run(args);
+        Log.flush();
+        return status;
+    }
+
+    private static int run(String... args) throws Exception {
+        String[] newArgs = CommandLine.parse(args);
+        if (newArgs.length == 0) {
+            CLIHelp.showHelp(true);
+        } else if (hasHelp(newArgs)){
+            if (hasVersion(newArgs)) {
+                Log.info(version + "\n");
+            }
+            CLIHelp.showHelp(false);
+        } else if (hasVersion(newArgs)) {
+            Log.info(version);
+        } else {
+            try {
+                Arguments arguments = new Arguments(newArgs);
+                if (!arguments.processArguments()) {
+                    // processArguments() should log error message if failed.
+                    return -1;
+                }
+            } catch (Exception e) {
+                if (Log.isVerbose()) {
+                    Log.verbose(e);
+                } else {
+                    Log.error(e.getMessage());
+                    if (e.getCause() != null && e.getCause() != e) {
+                        Log.error(e.getCause().getMessage());
+                    }
+                }
+                return -1;
+            }
+        }
+
+        return 0;
+    }
+
+    private static boolean hasHelp(String[] args) {
+        for (String a : args) {
+            if ("--help".equals(a) || "-h".equals(a)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static boolean hasVersion(String[] args) {
+        for (String a : args) {
+            if ("--version".equals(a) || "-v".equals(a)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/classes/module-info.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * Defines the Java Packaging tool, jpackage.
+ *
+ * <p>jpackage is a tool for generating self-contained application bundles.
+ *
+ * <p> This module provides the equivalent of command-line access to <em>jpackage</em>
+ * via the {@link java.util.spi.ToolProvider ToolProvider} SPI.
+ * Instances of the tool can be obtained by calling
+ * {@link java.util.spi.ToolProvider#findFirst ToolProvider.findFirst}
+ * or the {@link java.util.ServiceLoader service loader} with the name
+ * {@code "jpackage"}.
+ *
+ * @moduleGraph
+ * @since 13
+ */
+
+module jdk.jpackage {
+    requires jdk.jlink;
+
+    requires java.xml;
+    requires java.logging;
+    requires java.desktop;
+
+    uses jdk.jpackage.internal.Bundler;
+    uses jdk.jpackage.internal.Bundlers;
+
+    provides jdk.jpackage.internal.Bundlers with
+        jdk.jpackage.internal.BasicBundlers;
+
+    provides java.util.spi.ToolProvider
+        with jdk.jpackage.internal.JPackageToolProvider;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/native/libapplauncher/FileAttributes.h	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2014, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef FILEATTRIBUTES_H
+#define FILEATTRIBUTES_H
+
+#include "Platform.h"
+#include "PlatformString.h"
+#include "FileAttribute.h"
+
+#include <vector>
+
+class FileAttributes {
+private:
+    TString FFileName;
+    bool FFollowLink;
+    std::vector<FileAttribute> FAttributes;
+
+    bool WriteAttributes();
+    bool ReadAttributes();
+    bool Valid(const FileAttribute Value);
+
+public:
+    FileAttributes(const TString FileName, bool FollowLink = true);
+
+    void Append(const FileAttribute Value);
+    bool Contains(const FileAttribute Value);
+    void Remove(const FileAttribute Value);
+};
+
+#endif // FILEATTRIBUTES_H
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/native/libapplauncher/FilePath.h	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2014, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef FILEPATH_H
+#define FILEPATH_H
+
+#include "Platform.h"
+#include "PlatformString.h"
+#include "FileAttribute.h"
+
+#include <vector>
+
+class FileAttributes {
+private:
+    TString FFileName;
+    bool FFollowLink;
+    std::vector<FileAttribute> FAttributes;
+
+    bool WriteAttributes();
+    bool ReadAttributes();
+    bool Valid(const FileAttribute Value);
+
+public:
+    FileAttributes(const TString FileName, bool FollowLink = true);
+
+    void Append(const FileAttribute Value);
+    bool Contains(const FileAttribute Value);
+    void Remove(const FileAttribute Value);
+};
+
+class FilePath {
+private:
+    FilePath(void) {}
+    ~FilePath(void) {}
+
+public:
+    static bool FileExists(const TString FileName);
+    static bool DirectoryExists(const TString DirectoryName);
+
+    static bool DeleteFile(const TString FileName);
+    static bool DeleteDirectory(const TString DirectoryName);
+
+    static TString ExtractFilePath(TString Path);
+    static TString ExtractFileExt(TString Path);
+    static TString ExtractFileName(TString Path);
+    static TString ChangeFileExt(TString Path, TString Extension);
+
+    static TString IncludeTrailingSeparator(const TString value);
+    static TString IncludeTrailingSeparator(const char* value);
+    static TString IncludeTrailingSeparator(const wchar_t* value);
+    static TString FixPathForPlatform(TString Path);
+    static TString FixPathSeparatorForPlatform(TString Path);
+    static TString PathSeparator();
+
+    static bool CreateDirectory(TString Path, bool ownerOnly);
+    static void ChangePermissions(TString FileName, bool ownerOnly);
+};
+
+#endif //FILEPATH_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/native/libapplauncher/Helpers.cpp	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,296 @@
+/*
+ * Copyright (c) 2014, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "Helpers.h"
+#include "PlatformString.h"
+#include "PropertyFile.h"
+
+
+bool Helpers::SplitOptionIntoNameValue(
+        TString option, TString& Name, TString& Value) {
+    bool hasValue = false;
+    Name = _T("");
+    Value = _T("");
+    unsigned int index = 0;
+
+    for (; index < option.length(); index++) {
+        TCHAR c = option[index];
+
+        switch (c) {
+            case '=': {
+                index++;
+                hasValue = true;
+                break;
+            }
+
+            case '\\': {
+                if (index + 1 < option.length()) {
+                    c = option[index + 1];
+
+                    switch (c) {
+                        case '\\': {
+                            index++;
+                            Name += '\\';
+                            break;
+                        }
+
+                        case '=': {
+                            index++;
+                            Name += '=';
+                            break;
+                        }
+                    }
+
+                }
+
+                continue;
+            }
+
+            default: {
+                Name += c;
+                continue;
+            }
+        }
+
+        break;
+    }
+
+    if (hasValue) {
+        Value = option.substr(index, index - option.length());
+    }
+
+    return (option.length() > 0);
+}
+
+
+TString Helpers::ReplaceString(TString subject, const TString& search,
+                            const TString& replace) {
+    size_t pos = 0;
+    while((pos = subject.find(search, pos)) != TString::npos) {
+            subject.replace(pos, search.length(), replace);
+            pos += replace.length();
+    }
+    return subject;
+}
+
+TString Helpers::ConvertIdToFilePath(TString Value) {
+    TString search;
+    search = '.';
+    TString replace;
+    replace = '/';
+    TString result = ReplaceString(Value, search, replace);
+    return result;
+}
+
+TString Helpers::ConvertIdToJavaPath(TString Value) {
+    TString search;
+    search = '.';
+    TString replace;
+    replace = '/';
+    TString result = ReplaceString(Value, search, replace);
+    search = '\\';
+    result = ReplaceString(result, search, replace);
+    return result;
+}
+
+TString Helpers::ConvertJavaPathToId(TString Value) {
+    TString search;
+    search = '/';
+    TString replace;
+    replace = '.';
+    TString result = ReplaceString(Value, search, replace);
+    return result;
+}
+
+OrderedMap<TString, TString>
+        Helpers::GetJVMArgsFromConfig(IPropertyContainer* config) {
+    OrderedMap<TString, TString> result;
+
+    for (unsigned int index = 0; index < config->GetCount(); index++) {
+        TString argname =
+                TString(_T("jvmarg.")) + PlatformString(index + 1).toString();
+        TString argvalue;
+
+        if (config->GetValue(argname, argvalue) == false) {
+            break;
+        }
+        else if (argvalue.empty() == false) {
+            TString name;
+            TString value;
+            if (Helpers::SplitOptionIntoNameValue(argvalue, name, value)) {
+                result.Append(name, value);
+            }
+        }
+    }
+
+    return result;
+}
+
+std::list<TString> Helpers::GetArgsFromConfig(IPropertyContainer* config) {
+    std::list<TString> result;
+
+    for (unsigned int index = 0; index < config->GetCount(); index++) {
+        TString argname = TString(_T("arg."))
+                + PlatformString(index + 1).toString();
+        TString argvalue;
+
+        if (config->GetValue(argname, argvalue) == false) {
+            break;
+        }
+        else if (argvalue.empty() == false) {
+            result.push_back((argvalue));
+        }
+    }
+
+    return result;
+}
+
+void AppendToIni(PropertyFile &Source, IniFile* Destination, TString Key) {
+    TString value;
+
+    if (Source.GetValue(Key, value) == true) {
+        Platform& platform = Platform::GetInstance();
+        std::map<TString, TString> keys = platform.GetKeys();
+        Destination->Append(keys[CONFIG_SECTION_APPLICATION], Key, value);
+    }
+}
+
+void Helpers::LoadOldConfigFile(TString FileName, IniFile* Container) {
+    PropertyFile propertyFile;
+
+    if (propertyFile.LoadFromFile(FileName) == true) {
+        Platform& platform = Platform::GetInstance();
+
+        std::map<TString, TString> keys = platform.GetKeys();
+
+        // Application Section
+        AppendToIni(propertyFile, Container, keys[CONFIG_MAINJAR_KEY]);
+        AppendToIni(propertyFile, Container, keys[CONFIG_MAINMODULE_KEY]);
+        AppendToIni(propertyFile, Container, keys[CONFIG_MAINCLASSNAME_KEY]);
+        AppendToIni(propertyFile, Container, keys[CONFIG_CLASSPATH_KEY]);
+        AppendToIni(propertyFile, Container, keys[APP_NAME_KEY]);
+        AppendToIni(propertyFile, Container, keys[CONFIG_APP_ID_KEY]);
+        AppendToIni(propertyFile, Container, keys[JVM_RUNTIME_KEY]);
+        AppendToIni(propertyFile, Container, keys[JPACKAGE_APP_DATA_DIR]);
+
+        AppendToIni(propertyFile, Container, keys[CONFIG_APP_MEMORY]);
+        AppendToIni(propertyFile, Container, keys[CONFIG_SPLASH_KEY]);
+
+        // JVMOptions Section
+        OrderedMap<TString, TString> JVMArgs =
+                Helpers::GetJVMArgsFromConfig(&propertyFile);
+        Container->AppendSection(keys[CONFIG_SECTION_JVMOPTIONS], JVMArgs);
+
+        // ArgOptions Section
+        std::list<TString> args = Helpers::GetArgsFromConfig(&propertyFile);
+        OrderedMap<TString, TString> convertedArgs;
+
+        for (std::list<TString>::iterator iterator = args.begin();
+                iterator != args.end(); iterator++) {
+            TString arg = *iterator;
+            TString name;
+            TString value;
+
+            if (Helpers::SplitOptionIntoNameValue(arg, name, value) == true) {
+                convertedArgs.Append(name, value);
+            }
+        }
+
+        Container->AppendSection(keys[CONFIG_SECTION_ARGOPTIONS],
+                convertedArgs);
+    }
+}
+
+std::list<TString>
+        Helpers::MapToNameValueList(OrderedMap<TString, TString> Map) {
+    std::list<TString> result;
+    std::vector<TString> keys = Map.GetKeys();
+
+    for (OrderedMap<TString, TString>::const_iterator iterator = Map.begin();
+            iterator != Map.end(); iterator++) {
+       JPPair<TString, TString> *item = *iterator;
+       TString key = item->first;
+       TString value = item->second;
+
+       if (value.length() == 0) {
+           result.push_back(key);
+       } else {
+           result.push_back(key + _T('=') + value);
+        }
+    }
+
+    return result;
+}
+
+TString Helpers::NameValueToString(TString name, TString value) {
+    TString result;
+
+    if (value.empty() == true) {
+        result = name;
+    }
+    else {
+        result = name + TString(_T("=")) + value;
+    }
+
+    return result;
+}
+
+std::list<TString> Helpers::StringToArray(TString Value) {
+    std::list<TString> result;
+    TString line;
+
+    for (unsigned int index = 0; index < Value.length(); index++) {
+        TCHAR c = Value[index];
+
+        switch (c) {
+            case '\n': {
+                result.push_back(line);
+                line = _T("");
+                break;
+            }
+
+            case '\r': {
+                result.push_back(line);
+                line = _T("");
+
+                if (Value[index + 1] == '\n')
+                    index++;
+
+                break;
+            }
+
+            default: {
+                line += c;
+            }
+        }
+    }
+
+    // The buffer may not have ended with a Carriage Return/Line Feed.
+    if (line.length() > 0) {
+        result.push_back(line);
+    }
+
+    return result;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/native/libapplauncher/Helpers.h	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2014, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef HELPERS_H
+#define HELPERS_H
+
+#include "Platform.h"
+#include "OrderedMap.h"
+#include "IniFile.h"
+
+
+class Helpers {
+private:
+    Helpers(void) {}
+    ~Helpers(void) {}
+
+public:
+    // Supports two formats for option:
+    // Example 1:
+    // foo=bar
+    //
+    // Example 2:
+    // <name=foo=, value=goo>
+    static bool SplitOptionIntoNameValue(TString option,
+            TString& Name, TString& Value);
+    static TString ReplaceString(TString subject, const TString& search,
+            const TString& replace);
+    static TString ConvertIdToFilePath(TString Value);
+    static TString ConvertIdToJavaPath(TString Value);
+    static TString ConvertJavaPathToId(TString Value);
+
+    static OrderedMap<TString, TString>
+            GetJVMArgsFromConfig(IPropertyContainer* config);
+    static std::list<TString> GetArgsFromConfig(IPropertyContainer* config);
+
+    static void LoadOldConfigFile(TString FileName, IniFile* Container);
+
+    static std::list<TString>
+            MapToNameValueList(OrderedMap<TString, TString> Map);
+
+    static TString NameValueToString(TString name, TString value);
+
+    static std::list<TString> StringToArray(TString Value);
+};
+
+#endif // HELPERS_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/native/libapplauncher/IniFile.cpp	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,261 @@
+/*
+ * Copyright (c) 2015, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "IniFile.h"
+#include "Helpers.h"
+
+#include <string>
+
+
+IniFile::IniFile() : ISectionalPropertyContainer() {
+}
+
+IniFile::~IniFile() {
+    for (OrderedMap<TString, IniSectionData*>::iterator iterator =
+            FMap.begin(); iterator != FMap.end(); iterator++) {
+        JPPair<TString, IniSectionData*> *item = *iterator;
+        delete item->second;
+    }
+}
+
+bool IniFile::LoadFromFile(const TString FileName) {
+    bool result = false;
+    Platform& platform = Platform::GetInstance();
+
+    std::list<TString> contents = platform.LoadFromFile(FileName);
+
+    if (contents.empty() == false) {
+        bool found = false;
+
+        // Determine the if file is an INI file or property file.
+        // Assign FDefaultSection if it is
+        // an INI file. Otherwise FDefaultSection is NULL.
+        for (std::list<TString>::const_iterator iterator = contents.begin();
+                iterator != contents.end(); iterator++) {
+            TString line = *iterator;
+
+            if (line[0] == ';') {
+                // Semicolon is a comment so ignore the line.
+                continue;
+            }
+            else {
+                if (line[0] == '[') {
+                    found = true;
+                }
+
+                break;
+            }
+        }
+
+        if (found == true) {
+            TString sectionName;
+
+            for (std::list<TString>::const_iterator iterator = contents.begin();
+                    iterator != contents.end(); iterator++) {
+                TString line = *iterator;
+
+                if (line[0] == ';') {
+                    // Semicolon is a comment so ignore the line.
+                    continue;
+                }
+                else if (line[0] == '[' && line[line.length() - 1] == ']') {
+                    sectionName = line.substr(1, line.size() - 2);
+                }
+                else if (sectionName.empty() == false) {
+                    TString name;
+                    TString value;
+
+                    if (Helpers::SplitOptionIntoNameValue(
+                            line, name, value) == true) {
+                        Append(sectionName, name, value);
+                    }
+                }
+            }
+
+            result = true;
+        }
+    }
+
+    return result;
+}
+
+bool IniFile::SaveToFile(const TString FileName, bool ownerOnly) {
+    bool result = false;
+
+    std::list<TString> contents;
+    std::vector<TString> keys = FMap.GetKeys();
+
+    for (unsigned int index = 0; index < keys.size(); index++) {
+        TString name = keys[index];
+        IniSectionData *section;
+
+        if (FMap.GetValue(name, section) == true) {
+            contents.push_back(_T("[") + name + _T("]"));
+            std::list<TString> lines = section->GetLines();
+            contents.insert(contents.end(), lines.begin(), lines.end());
+            contents.push_back(_T(""));
+        }
+    }
+
+    Platform& platform = Platform::GetInstance();
+    platform.SaveToFile(FileName, contents, ownerOnly);
+    result = true;
+    return result;
+}
+
+void IniFile::Append(const TString SectionName,
+        const TString Key, TString Value) {
+    if (FMap.ContainsKey(SectionName) == true) {
+        IniSectionData* section;
+
+        if (FMap.GetValue(SectionName, section) == true && section != NULL) {
+            section->SetValue(Key, Value);
+        }
+    }
+    else {
+        IniSectionData *section = new IniSectionData();
+        section->SetValue(Key, Value);
+        FMap.Append(SectionName, section);
+    }
+}
+
+void IniFile::AppendSection(const TString SectionName,
+        OrderedMap<TString, TString> Values) {
+    if (FMap.ContainsKey(SectionName) == true) {
+        IniSectionData* section;
+
+        if (FMap.GetValue(SectionName, section) == true && section != NULL) {
+            section->Append(Values);
+        }
+    }
+    else {
+        IniSectionData *section = new IniSectionData(Values);
+        FMap.Append(SectionName, section);
+    }
+}
+
+bool IniFile::GetValue(const TString SectionName,
+        const TString Key, TString& Value) {
+    bool result = false;
+    IniSectionData* section;
+
+    if (FMap.GetValue(SectionName, section) == true && section != NULL) {
+        result = section->GetValue(Key, Value);
+    }
+
+    return result;
+}
+
+bool IniFile::SetValue(const TString SectionName,
+        const TString Key, TString Value) {
+    bool result = false;
+    IniSectionData* section;
+
+    if (FMap.GetValue(SectionName, section) && section != NULL) {
+        result = section->SetValue(Key, Value);
+    }
+    else {
+        Append(SectionName, Key, Value);
+    }
+
+
+    return result;
+}
+
+bool IniFile::GetSection(const TString SectionName,
+        OrderedMap<TString, TString> &Data) {
+    bool result = false;
+
+    if (FMap.ContainsKey(SectionName) == true) {
+        IniSectionData* section;
+
+        if (FMap.GetValue(SectionName, section) == true && section != NULL) {
+            OrderedMap<TString, TString> data = section->GetData();
+            Data.Append(data);
+            result = true;
+        }
+    }
+
+    return result;
+}
+
+bool IniFile::ContainsSection(const TString SectionName) {
+    return FMap.ContainsKey(SectionName);
+}
+
+//----------------------------------------------------------------------------
+
+IniSectionData::IniSectionData() {
+    FMap.SetAllowDuplicates(true);
+}
+
+IniSectionData::IniSectionData(OrderedMap<TString, TString> Values) {
+    FMap = Values;
+}
+
+std::vector<TString> IniSectionData::GetKeys() {
+    return FMap.GetKeys();
+}
+
+std::list<TString> IniSectionData::GetLines() {
+    std::list<TString> result;
+    std::vector<TString> keys = FMap.GetKeys();
+
+    for (unsigned int index = 0; index < keys.size(); index++) {
+        TString name = keys[index];
+        TString value;
+
+        if (FMap.GetValue(name, value) == true) {
+            name = Helpers::ReplaceString(name, _T("="), _T("\\="));
+            value = Helpers::ReplaceString(value, _T("="), _T("\\="));
+
+            TString line = name + _T('=') + value;
+            result.push_back(line);
+        }
+    }
+
+    return result;
+}
+
+OrderedMap<TString, TString> IniSectionData::GetData() {
+    OrderedMap<TString, TString> result = FMap;
+    return result;
+}
+
+bool IniSectionData::GetValue(const TString Key, TString& Value) {
+    return FMap.GetValue(Key, Value);
+}
+
+bool IniSectionData::SetValue(const TString Key, TString Value) {
+    return FMap.SetValue(Key, Value);
+}
+
+void IniSectionData::Append(OrderedMap<TString, TString> Values) {
+    FMap.Append(Values);
+}
+
+size_t IniSectionData::GetCount() {
+    return FMap.Count();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/native/libapplauncher/IniFile.h	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2015, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef INIFILE_H
+#define INIFILE_H
+
+#include "Platform.h"
+#include "OrderedMap.h"
+
+#include <map>
+
+
+class IniSectionData : public IPropertyContainer {
+private:
+    OrderedMap<TString, TString> FMap;
+
+public:
+    IniSectionData();
+    IniSectionData(OrderedMap<TString, TString> Values);
+
+    std::vector<TString> GetKeys();
+    std::list<TString> GetLines();
+    OrderedMap<TString, TString> GetData();
+
+    bool SetValue(const TString Key, TString Value);
+    void Append(OrderedMap<TString, TString> Values);
+
+    virtual bool GetValue(const TString Key, TString& Value);
+    virtual size_t GetCount();
+};
+
+
+class IniFile : public ISectionalPropertyContainer {
+private:
+    OrderedMap<TString, IniSectionData*> FMap;
+
+public:
+    IniFile();
+    virtual ~IniFile();
+
+    void internalTest();
+
+    bool LoadFromFile(const TString FileName);
+    bool SaveToFile(const TString FileName, bool ownerOnly = true);
+
+    void Append(const TString SectionName, const TString Key, TString Value);
+    void AppendSection(const TString SectionName,
+            OrderedMap<TString, TString> Values);
+    bool SetValue(const TString SectionName,
+            const TString Key, TString Value);
+
+    // ISectionalPropertyContainer
+    virtual bool GetSection(const TString SectionName,
+            OrderedMap<TString, TString> &Data);
+    virtual bool ContainsSection(const TString SectionName);
+    virtual bool GetValue(const TString SectionName,
+            const TString Key, TString& Value);
+};
+
+#endif // INIFILE_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/native/libapplauncher/JavaVirtualMachine.cpp	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,304 @@
+/*
+ * Copyright (c) 2014, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "JavaVirtualMachine.h"
+#include "Platform.h"
+#include "PlatformString.h"
+#include "FilePath.h"
+#include "Package.h"
+#include "Helpers.h"
+#include "Messages.h"
+#include "Macros.h"
+
+#include "jni.h"
+
+#include <map>
+#include <list>
+#include <sstream>
+
+
+bool RunVM() {
+    JavaVirtualMachine javavm;
+
+    bool result = javavm.StartJVM();
+
+    if (!result) {
+        Platform& platform = Platform::GetInstance();
+        platform.ShowMessage(_T("Failed to launch JVM\n"));
+    }
+
+    return result;
+}
+
+//----------------------------------------------------------------------------
+
+JavaOptions::JavaOptions(): FOptions(NULL) {
+}
+
+JavaOptions::~JavaOptions() {
+    if (FOptions != NULL) {
+        for (unsigned int index = 0; index < GetCount(); index++) {
+            delete[] FOptions[index].optionString;
+        }
+
+        delete[] FOptions;
+    }
+}
+
+void JavaOptions::AppendValue(const TString Key, TString Value, void* Extra) {
+    JavaOptionItem item;
+    item.name = Key;
+    item.value = Value;
+    item.extraInfo = Extra;
+    FItems.push_back(item);
+}
+
+void JavaOptions::AppendValue(const TString Key, TString Value) {
+    AppendValue(Key, Value, NULL);
+}
+
+void JavaOptions::AppendValue(const TString Key) {
+    AppendValue(Key, _T(""), NULL);
+}
+
+void JavaOptions::AppendValues(OrderedMap<TString, TString> Values) {
+    std::vector<TString> orderedKeys = Values.GetKeys();
+
+    for (std::vector<TString>::const_iterator iterator = orderedKeys.begin();
+        iterator != orderedKeys.end(); iterator++) {
+        TString name = *iterator;
+        TString value;
+
+        if (Values.GetValue(name, value) == true) {
+            AppendValue(name, value);
+        }
+    }
+}
+
+void JavaOptions::ReplaceValue(const TString Key, TString Value) {
+    for (std::list<JavaOptionItem>::iterator iterator = FItems.begin();
+        iterator != FItems.end(); iterator++) {
+
+        TString lkey = iterator->name;
+
+        if (lkey == Key) {
+            JavaOptionItem item = *iterator;
+            item.value = Value;
+            iterator = FItems.erase(iterator);
+            FItems.insert(iterator, item);
+            break;
+        }
+    }
+}
+
+std::list<TString> JavaOptions::ToList() {
+    std::list<TString> result;
+    Macros& macros = Macros::GetInstance();
+
+    for (std::list<JavaOptionItem>::const_iterator iterator = FItems.begin();
+        iterator != FItems.end(); iterator++) {
+        TString key = iterator->name;
+        TString value = iterator->value;
+        TString option = Helpers::NameValueToString(key, value);
+        option = macros.ExpandMacros(option);
+        result.push_back(option);
+    }
+
+    return result;
+}
+
+size_t JavaOptions::GetCount() {
+    return FItems.size();
+}
+
+//----------------------------------------------------------------------------
+
+JavaVirtualMachine::JavaVirtualMachine() {
+}
+
+JavaVirtualMachine::~JavaVirtualMachine(void) {
+}
+
+bool JavaVirtualMachine::StartJVM() {
+    Platform& platform = Platform::GetInstance();
+    Package& package = Package::GetInstance();
+
+    TString classpath = package.GetClassPath();
+    TString modulepath = package.GetModulePath();
+    JavaOptions options;
+
+    if (modulepath.empty() == false) {
+        options.AppendValue(_T("-Djava.module.path"), modulepath);
+    }
+
+    options.AppendValue(_T("-Djava.library.path"),
+            package.GetPackageAppDirectory() + FilePath::PathSeparator()
+            + package.GetPackageLauncherDirectory());
+    options.AppendValue(
+            _T("-Djava.launcher.path"), package.GetPackageLauncherDirectory());
+    options.AppendValue(_T("-Dapp.preferences.id"), package.GetAppID());
+    options.AppendValues(package.GetJVMArgs());
+
+#ifdef DEBUG
+    if (package.Debugging() == dsJava) {
+        options.AppendValue(_T("-Xdebug"), _T(""));
+        options.AppendValue(
+                _T("-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=localhost:5005"),
+                _T(""));
+        platform.ShowMessage(_T("localhost:5005"));
+    }
+#endif // DEBUG
+
+    TString maxHeapSizeOption;
+    TString minHeapSizeOption;
+
+
+    if (package.GetMemoryState() == PackageBootFields::msAuto) {
+        TPlatformNumber memorySize = package.GetMemorySize();
+        TString memory =
+                PlatformString((size_t)memorySize).toString() + _T("m");
+        maxHeapSizeOption = TString(_T("-Xmx")) + memory;
+        options.AppendValue(maxHeapSizeOption, _T(""));
+
+        if (memorySize > 256)
+            minHeapSizeOption = _T("-Xms256m");
+        else
+            minHeapSizeOption = _T("-Xms") + memory;
+
+        options.AppendValue(minHeapSizeOption, _T(""));
+    }
+
+    TString mainClassName = package.GetMainClassName();
+    TString mainModule = package.GetMainModule();
+
+    if (mainClassName.empty() == true && mainModule.empty() == true) {
+        Messages& messages = Messages::GetInstance();
+        platform.ShowMessage(messages.GetMessage(NO_MAIN_CLASS_SPECIFIED));
+        return false;
+    }
+
+    configureLibrary();
+
+    // Initialize the arguments to JLI_Launch()
+    //
+    // On Mac OS X JLI_Launch spawns a new thread that actually starts the JVM.
+    // This new thread simply re-runs main(argc, argv). Therefore we do not
+    // want to add new args if we are still in the original main thread so we
+    // will treat them as command line args provided by the user ...
+    // Only propagate original set of args first time.
+
+    options.AppendValue(_T("-classpath"));
+    options.AppendValue(classpath);
+
+    std::list<TString> vmargs;
+    vmargs.push_back(package.GetCommandName());
+
+    if (package.HasSplashScreen() == true) {
+        options.AppendValue(TString(_T("-splash:"))
+                + package.GetSplashScreenFileName(), _T(""));
+    }
+
+    if (mainModule.empty() == true) {
+        options.AppendValue(Helpers::ConvertJavaPathToId(mainClassName),
+                _T(""));
+    } else {
+        options.AppendValue(_T("-m"));
+        options.AppendValue(mainModule);
+    }
+
+    return launchVM(options, vmargs);
+}
+
+void JavaVirtualMachine::configureLibrary() {
+    Platform& platform = Platform::GetInstance();
+    Package& package = Package::GetInstance();
+    TString libName = package.GetJVMLibraryFileName();
+    platform.addPlatformDependencies(&javaLibrary);
+    javaLibrary.Load(libName);
+}
+
+bool JavaVirtualMachine::launchVM(JavaOptions& options,
+        std::list<TString>& vmargs) {
+    Platform& platform = Platform::GetInstance();
+    Package& package = Package::GetInstance();
+
+#ifdef MAC
+    // Mac adds a ProcessSerialNumber to args when launched from .app
+    // filter out the psn since they it's not expected in the app
+    if (platform.IsMainThread() == false) {
+        std::list<TString> loptions = options.ToList();
+        vmargs.splice(vmargs.end(), loptions,
+                loptions.begin(), loptions.end());
+    }
+#else
+    std::list<TString> loptions = options.ToList();
+    vmargs.splice(vmargs.end(), loptions, loptions.begin(), loptions.end());
+#endif
+
+    std::list<TString> largs = package.GetArgs();
+    vmargs.splice(vmargs.end(), largs, largs.begin(), largs.end());
+
+    size_t argc = vmargs.size();
+    DynamicBuffer<char*> argv(argc + 1);
+    if (argv.GetData() == NULL) {
+        return false;
+    }
+
+    unsigned int index = 0;
+    for (std::list<TString>::const_iterator iterator = vmargs.begin();
+        iterator != vmargs.end(); iterator++) {
+        TString item = *iterator;
+        std::string arg = PlatformString(item).toStdString();
+#ifdef DEBUG
+        printf("%i %s\n", index, arg.c_str());
+#endif // DEBUG
+        argv[index] = PlatformString::duplicate(arg.c_str());
+        index++;
+    }
+
+    argv[argc] = NULL;
+
+// On Mac we can only free the boot fields if the calling thread is
+// not the main thread.
+#ifdef MAC
+    if (platform.IsMainThread() == false) {
+        package.FreeBootFields();
+    }
+#else
+    package.FreeBootFields();
+#endif // MAC
+
+    if (javaLibrary.JavaVMCreate(argc, argv.GetData()) == true) {
+        return true;
+    }
+
+    for (index = 0; index < argc; index++) {
+        if (argv[index] != NULL) {
+            delete[] argv[index];
+        }
+    }
+
+    return false;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/native/libapplauncher/JavaVirtualMachine.h	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2014, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef JAVAVIRTUALMACHINE_H
+#define JAVAVIRTUALMACHINE_H
+
+
+#include "jni.h"
+#include "Platform.h"
+#include "Library.h"
+
+struct JavaOptionItem {
+    TString name;
+    TString value;
+    void* extraInfo;
+};
+
+class JavaOptions {
+private:
+    std::list<JavaOptionItem> FItems;
+    JavaVMOption* FOptions;
+
+public:
+    JavaOptions();
+    ~JavaOptions();
+
+    void AppendValue(const TString Key, TString Value, void* Extra);
+    void AppendValue(const TString Key, TString Value);
+    void AppendValue(const TString Key);
+    void AppendValues(OrderedMap<TString, TString> Values);
+    void ReplaceValue(const TString Key, TString Value);
+    std::list<TString> ToList();
+    size_t GetCount();
+};
+
+class JavaVirtualMachine {
+private:
+    JavaLibrary javaLibrary;
+
+    void configureLibrary();
+    bool launchVM(JavaOptions& options, std::list<TString>& vmargs);
+public:
+    JavaVirtualMachine();
+    ~JavaVirtualMachine(void);
+
+    bool StartJVM();
+};
+
+bool RunVM();
+
+#endif // JAVAVIRTUALMACHINE_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/native/libapplauncher/Library.cpp	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,188 @@
+/*
+ * Copyright (c) 2014, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "Library.h"
+#include "Platform.h"
+#include "Messages.h"
+#include "PlatformString.h"
+
+#include <fstream>
+#include <locale>
+
+Library::Library() {
+    Initialize();
+}
+
+Library::Library(const TString &FileName) {
+    Initialize();
+    Load(FileName);
+}
+
+Library::~Library() {
+    Unload();
+}
+
+void Library::Initialize() {
+    FModule = NULL;
+    FDependentLibraryNames = NULL;
+    FDependenciesLibraries = NULL;
+}
+
+void Library::InitializeDependencies() {
+    if (FDependentLibraryNames == NULL) {
+        FDependentLibraryNames = new std::vector<TString>();
+    }
+
+    if (FDependenciesLibraries == NULL) {
+        FDependenciesLibraries = new std::vector<Library*>();
+    }
+}
+
+void Library::LoadDependencies() {
+    if (FDependentLibraryNames != NULL && FDependenciesLibraries != NULL) {
+        for (std::vector<TString>::const_iterator iterator =
+                FDependentLibraryNames->begin();
+                iterator != FDependentLibraryNames->end(); iterator++) {
+            Library* library = new Library();
+
+            if (library->Load(*iterator) == true) {
+                FDependenciesLibraries->push_back(library);
+            }
+        }
+
+        delete FDependentLibraryNames;
+        FDependentLibraryNames = NULL;
+    }
+}
+
+void Library::UnloadDependencies() {
+    if (FDependenciesLibraries != NULL) {
+        for (std::vector<Library*>::const_iterator iterator =
+                FDependenciesLibraries->begin();
+                iterator != FDependenciesLibraries->end(); iterator++) {
+            Library* library = *iterator;
+
+            if (library != NULL) {
+                library->Unload();
+                delete library;
+            }
+        }
+
+        delete FDependenciesLibraries;
+        FDependenciesLibraries = NULL;
+    }
+}
+
+Procedure Library::GetProcAddress(const std::string& MethodName) const {
+    Platform& platform = Platform::GetInstance();
+    return platform.GetProcAddress(FModule, MethodName);
+}
+
+bool Library::Load(const TString &FileName) {
+    bool result = true;
+
+    if (FModule == NULL) {
+        LoadDependencies();
+        Platform& platform = Platform::GetInstance();
+        FModule = platform.LoadLibrary(FileName);
+
+        if (FModule == NULL) {
+            Messages& messages = Messages::GetInstance();
+            platform.ShowMessage(messages.GetMessage(LIBRARY_NOT_FOUND),
+                    FileName);
+            result = false;
+        } else {
+            fname = PlatformString(FileName).toStdString();
+        }
+    }
+
+    return result;
+}
+
+bool Library::Unload() {
+    bool result = false;
+
+    if (FModule != NULL) {
+        Platform& platform = Platform::GetInstance();
+        platform.FreeLibrary(FModule);
+        FModule = NULL;
+        UnloadDependencies();
+        result = true;
+    }
+
+    return result;
+}
+
+void Library::AddDependency(const TString &FileName) {
+    InitializeDependencies();
+
+    if (FDependentLibraryNames != NULL) {
+        FDependentLibraryNames->push_back(FileName);
+    }
+}
+
+void Library::AddDependencies(const std::vector<TString> &Dependencies) {
+    if (Dependencies.size() > 0) {
+        InitializeDependencies();
+
+        if (FDependentLibraryNames != NULL) {
+            for (std::vector<TString>::const_iterator iterator =
+                    FDependentLibraryNames->begin();
+                    iterator != FDependentLibraryNames->end(); iterator++) {
+                TString fileName = *iterator;
+                AddDependency(fileName);
+            }
+        }
+    }
+}
+
+JavaLibrary::JavaLibrary() : Library(), FCreateProc(NULL) {
+}
+
+bool JavaLibrary::JavaVMCreate(size_t argc, char *argv[]) {
+    if (FCreateProc == NULL) {
+        FCreateProc = (JVM_CREATE) GetProcAddress(LAUNCH_FUNC);
+    }
+
+    if (FCreateProc == NULL) {
+        Platform& platform = Platform::GetInstance();
+        Messages& messages = Messages::GetInstance();
+        platform.ShowMessage(
+                messages.GetMessage(FAILED_LOCATING_JVM_ENTRY_POINT));
+        return false;
+    }
+
+    return FCreateProc((int) argc, argv,
+            0, NULL,
+            0, NULL,
+            "",
+            "",
+            "java",
+            "java",
+            false,
+            false,
+            false,
+            0) == 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/native/libapplauncher/Library.h	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2014, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef LIBRARY_H
+#define LIBRARY_H
+
+#include "PlatformDefs.h"
+//#include "Platform.h"
+#include "OrderedMap.h"
+
+#include "jni.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <memory.h>
+#include <string>
+#include <map>
+#include <list>
+#include <vector>
+#include <fstream>
+
+using namespace std;
+
+// Private typedef for function pointer casting
+#define LAUNCH_FUNC "JLI_Launch"
+
+typedef int (JNICALL *JVM_CREATE)(int argc, char ** argv,
+        int jargc, const char** jargv,
+        int appclassc, const char** appclassv,
+        const char* fullversion,
+        const char* dotversion,
+        const char* pname,
+        const char* lname,
+        jboolean javaargs,
+        jboolean cpwildcard,
+        jboolean javaw,
+        jint ergo);
+
+class Library {
+private:
+    std::vector<TString> *FDependentLibraryNames;
+    std::vector<Library*> *FDependenciesLibraries;
+    Module FModule;
+    std::string fname;
+
+    void Initialize();
+    void InitializeDependencies();
+    void LoadDependencies();
+    void UnloadDependencies();
+
+public:
+    void* GetProcAddress(const std::string& MethodName) const;
+
+public:
+    Library();
+    Library(const TString &FileName);
+    ~Library();
+
+    bool Load(const TString &FileName);
+    bool Unload();
+
+    const std::string& GetName() const {
+        return fname;
+    }
+
+    void AddDependency(const TString &FileName);
+    void AddDependencies(const std::vector<TString> &Dependencies);
+};
+
+class JavaLibrary : public Library {
+    JVM_CREATE FCreateProc;
+    JavaLibrary(const TString &FileName);
+public:
+    JavaLibrary();
+    bool JavaVMCreate(size_t argc, char *argv[]);
+};
+
+#endif // LIBRARY_H
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/native/libapplauncher/Macros.cpp	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2014, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "Macros.h"
+#include "Package.h"
+#include "Helpers.h"
+
+
+Macros::Macros(void) {
+}
+
+Macros::~Macros(void) {
+}
+
+void Macros::Initialize() {
+    Package& package = Package::GetInstance();
+    Macros& macros = Macros::GetInstance();
+
+    // Public macros.
+    macros.AddMacro(_T("$APPDIR"), package.GetPackageRootDirectory());
+    macros.AddMacro(_T("$PACKAGEDIR"), package.GetPackageAppDirectory());
+    macros.AddMacro(_T("$LAUNCHERDIR"), package.GetPackageLauncherDirectory());
+    macros.AddMacro(_T("$APPDATADIR"), package.GetAppDataDirectory());
+
+    TString javaHome =
+            FilePath::ExtractFilePath(package.GetJVMLibraryFileName());
+    macros.AddMacro(_T("$JREHOME"), javaHome);
+
+    // App CDS Macros
+    macros.AddMacro(_T("$CACHEDIR"), package.GetAppCDSCacheDirectory());
+
+    // Private macros.
+    TString javaVMLibraryName = FilePath::ExtractFileName(javaHome);
+    macros.AddMacro(_T("$JAVAVMLIBRARYNAME"), javaVMLibraryName);
+}
+
+Macros& Macros::GetInstance() {
+    static Macros instance;
+    return instance;
+}
+
+TString Macros::ExpandMacros(TString Value) {
+    TString result = Value;
+
+    for (std::map<TString, TString>::iterator iterator = FData.begin();
+        iterator != FData.end();
+        iterator++) {
+
+        TString name = iterator->first;
+
+        if (Value.find(name) != TString::npos) {
+            TString lvalue = iterator->second;
+            result = Helpers::ReplaceString(Value, name, lvalue);
+            result = ExpandMacros(result);
+            break;
+        }
+    }
+
+    return result;
+}
+
+void Macros::AddMacro(TString Key, TString Value) {
+    FData.insert(std::map<TString, TString>::value_type(Key, Value));
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/native/libapplauncher/Macros.h	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2014, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef MACROS_H
+#define MACROS_H
+
+#include "Platform.h"
+
+#include <map>
+
+
+class Macros {
+private:
+    std::map<TString, TString> FData;
+
+    Macros(void);
+
+public:
+    static Macros& GetInstance();
+    static void Initialize();
+    ~Macros(void);
+
+    TString ExpandMacros(TString Value);
+    void AddMacro(TString Key, TString Value);
+};
+
+#endif // MACROS_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/native/libapplauncher/Messages.cpp	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2014, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "Messages.h"
+#include "Platform.h"
+#include "FilePath.h"
+#include "Helpers.h"
+#include "Macros.h"
+#include "JavaVirtualMachine.h"
+
+Messages::Messages(void) {
+    FMessages.SetReadOnly(false);
+    FMessages.SetValue(LIBRARY_NOT_FOUND, _T("Failed to find library."));
+    FMessages.SetValue(FAILED_CREATING_JVM, _T("Failed to create JVM"));
+    FMessages.SetValue(FAILED_LOCATING_JVM_ENTRY_POINT,
+            _T("Failed to locate JLI_Launch"));
+    FMessages.SetValue(NO_MAIN_CLASS_SPECIFIED, _T("No main class specified"));
+    FMessages.SetValue(METHOD_NOT_FOUND, _T("No method %s in class %s."));
+    FMessages.SetValue(CLASS_NOT_FOUND, _T("Class %s not found."));
+    FMessages.SetValue(ERROR_INVOKING_METHOD, _T("Error invoking method."));
+    FMessages.SetValue(APPCDS_CACHE_FILE_NOT_FOUND,
+            _T("Error: AppCDS cache does not exists:\n%s\n"));
+}
+
+Messages& Messages::GetInstance() {
+    static Messages instance;
+    // Guaranteed to be destroyed. Instantiated on first use.
+    return instance;
+}
+
+Messages::~Messages(void) {
+}
+
+TString Messages::GetMessage(const TString Key) {
+    TString result;
+    FMessages.GetValue(Key, result);
+    Macros& macros = Macros::GetInstance();
+    result = macros.ExpandMacros(result);
+    return result;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/native/libapplauncher/Messages.h	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2014, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef MESSAGES_H
+#define MESSAGES_H
+
+#include "PropertyFile.h"
+
+#define LIBRARY_NOT_FOUND _T("library.not.found")
+#define FAILED_CREATING_JVM _T("failed.creating.jvm")
+#define FAILED_LOCATING_JVM_ENTRY_POINT _T("failed.locating.jvm.entry.point")
+#define NO_MAIN_CLASS_SPECIFIED _T("no.main.class.specified")
+
+#define METHOD_NOT_FOUND _T("method.not.found")
+#define CLASS_NOT_FOUND _T("class.not.found")
+#define ERROR_INVOKING_METHOD _T("error.invoking.method")
+
+#define CONFIG_FILE_NOT_FOUND _T("config.file.not.found")
+
+#define BUNDLED_JVM_NOT_FOUND _T("bundled.jvm.not.found")
+
+#define APPCDS_CACHE_FILE_NOT_FOUND _T("appcds.cache.file.not.found")
+
+class Messages {
+private:
+    PropertyFile FMessages;
+
+    Messages(void);
+public:
+    static Messages& GetInstance();
+    ~Messages(void);
+
+    TString GetMessage(const TString Key);
+};
+
+#endif // MESSAGES_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/native/libapplauncher/OrderedMap.h	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,239 @@
+/*
+ * Copyright (c) 2015, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef ORDEREDMAP_H
+#define ORDEREDMAP_H
+
+#include <map>
+#include <vector>
+#include <assert.h>
+#include <stdexcept>
+
+#include <iostream>
+
+template <typename _T1, typename _T2>
+struct JPPair
+{
+    typedef _T1 first_type;
+    typedef _T2 second_type;
+
+    first_type first;
+    second_type second;
+
+    JPPair(first_type Value1, second_type Value2) {
+        first = Value1;
+        second = Value2;
+    }
+};
+
+
+template <typename TKey, typename TValue>
+class OrderedMap {
+public:
+    typedef TKey key_type;
+    typedef TValue mapped_type;
+    typedef JPPair<key_type, mapped_type> container_type;
+    typedef typename std::vector<container_type*>::iterator iterator;
+    typedef typename std::vector<container_type*>::const_iterator const_iterator;
+
+private:
+    typedef std::map<key_type, container_type*> map_type;
+    typedef std::vector<container_type*> list_type;
+
+    map_type FMap;
+    list_type FList;
+    bool FAllowDuplicates;
+
+    typename list_type::iterator FindListItem(const key_type Key) {
+        typename list_type::iterator result = FList.end();
+
+        for (typename list_type::iterator iterator =
+                FList.begin(); iterator != FList.end(); iterator++) {
+            container_type *item = *iterator;
+
+            if (item->first == Key) {
+                result = iterator;
+                break;
+            }
+        }
+
+        return result;
+    }
+
+public:
+    OrderedMap() {
+        FAllowDuplicates = false;
+    }
+
+    OrderedMap(const OrderedMap<key_type, mapped_type> &Value) {
+        Append(Value);
+    }
+
+    ~OrderedMap() {
+        Clear();
+    }
+
+    void SetAllowDuplicates(bool Value) {
+        FAllowDuplicates = Value;
+    }
+
+    iterator begin() {
+        return FList.begin();
+    }
+
+    const_iterator begin() const {
+        return FList.begin();
+    }
+
+    iterator end() {
+        return FList.end();
+    }
+
+    const_iterator end() const {
+        return FList.end();
+    }
+
+    void Clear() {
+        for (typename list_type::iterator iterator =
+                FList.begin(); iterator != FList.end(); iterator++) {
+            container_type *item = *iterator;
+
+            if (item != NULL) {
+                delete item;
+                item = NULL;
+            }
+        }
+
+        FMap.clear();
+        FList.clear();
+    }
+
+    bool ContainsKey(key_type Key) {
+        bool result = false;
+
+        if (FMap.find(Key) != FMap.end()) {
+            result = true;
+        }
+
+        return result;
+    }
+
+    std::vector<key_type> GetKeys() {
+        std::vector<key_type> result;
+
+        for (typename list_type::const_iterator iterator = FList.begin();
+             iterator != FList.end(); iterator++) {
+            container_type *item = *iterator;
+            result.push_back(item->first);
+        }
+
+        return result;
+    }
+
+    void Assign(const OrderedMap<key_type, mapped_type> &Value) {
+        Clear();
+        Append(Value);
+    }
+
+    void Append(const OrderedMap<key_type, mapped_type> &Value) {
+        for (size_t index = 0; index < Value.FList.size(); index++) {
+            container_type *item = Value.FList[index];
+            Append(item->first, item->second);
+        }
+    }
+
+    void Append(key_type Key, mapped_type Value) {
+        container_type *item = new container_type(Key, Value);
+        FMap.insert(std::pair<key_type, container_type*>(Key, item));
+        FList.push_back(item);
+    }
+
+    bool RemoveByKey(key_type Key) {
+        bool result = false;
+        typename list_type::iterator iterator = FindListItem(Key);
+
+        if (iterator != FList.end()) {
+            FMap.erase(Key);
+            FList.erase(iterator);
+            result = true;
+        }
+
+        return result;
+    }
+
+    bool GetValue(key_type Key, mapped_type &Value) {
+        bool result = false;
+        container_type* item = FMap[Key];
+
+        if (item != NULL) {
+            Value = item->second;
+            result = true;
+        }
+
+        return result;
+    }
+
+    bool SetValue(key_type Key, mapped_type &Value) {
+        bool result = false;
+
+        if ((FAllowDuplicates == false) && (ContainsKey(Key) == true)) {
+            container_type *item = FMap[Key];
+
+            if (item != NULL) {
+                item->second = Value;
+                result = true;
+            }
+        }
+        else {
+            Append(Key, Value);
+            result = true;
+        }
+
+        return result;
+    }
+
+    mapped_type &operator[](key_type Key) {
+        container_type* item = FMap[Key];
+        assert(item != NULL);
+
+        if (item != NULL) {
+            return item->second;
+        }
+
+        throw std::invalid_argument("Key not found");
+    }
+
+    OrderedMap& operator= (OrderedMap &Value) {
+        Clear();
+        Append(Value);
+        return *this;
+    }
+
+    size_t Count() {
+        return FList.size();
+    }
+};
+
+#endif // ORDEREDMAP_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/native/libapplauncher/Package.cpp	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,563 @@
+/*
+ * Copyright (c) 2014, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "Package.h"
+#include "Helpers.h"
+#include "Macros.h"
+#include "IniFile.h"
+
+#include <assert.h>
+
+
+Package::Package(void) {
+    FInitialized = false;
+    Initialize();
+}
+
+TPlatformNumber StringToPercentageOfNumber(TString Value,
+        TPlatformNumber Number) {
+    TPlatformNumber result = 0;
+    size_t percentage = atoi(PlatformString(Value.c_str()));
+
+    if (percentage > 0 && Number > 0) {
+        result = Number * percentage / 100;
+    }
+
+    return result;
+}
+
+void Package::Initialize() {
+    if (FInitialized == true) {
+        return;
+    }
+
+    Platform& platform = Platform::GetInstance();
+
+    FBootFields = new PackageBootFields();
+    FDebugging = dsNone;
+
+    FBootFields->FPackageRootDirectory = platform.GetPackageRootDirectory();
+    FBootFields->FPackageAppDirectory = platform.GetPackageAppDirectory();
+    FBootFields->FPackageLauncherDirectory =
+            platform.GetPackageLauncherDirectory();
+    FBootFields->FAppDataDirectory = platform.GetAppDataDirectory();
+
+    std::map<TString, TString> keys = platform.GetKeys();
+
+    // Read from configure.cfg/Info.plist
+    AutoFreePtr<ISectionalPropertyContainer> config =
+            platform.GetConfigFile(platform.GetConfigFileName());
+
+    config->GetValue(keys[CONFIG_SECTION_APPLICATION],
+            keys[CONFIG_APP_ID_KEY], FBootFields->FAppID);
+    config->GetValue(keys[CONFIG_SECTION_APPLICATION],
+            keys[JPACKAGE_APP_DATA_DIR], FBootFields->FPackageAppDataDirectory);
+    FBootFields->FPackageAppDataDirectory =
+            FilePath::FixPathForPlatform(FBootFields->FPackageAppDataDirectory);
+
+    // Main JAR.
+    config->GetValue(keys[CONFIG_SECTION_APPLICATION],
+            keys[CONFIG_MAINJAR_KEY], FBootFields->FMainJar);
+    FBootFields->FMainJar =
+            FilePath::IncludeTrailingSeparator(GetPackageAppDirectory())
+            + FilePath::FixPathForPlatform(FBootFields->FMainJar);
+
+    // Main Module.
+    config->GetValue(keys[CONFIG_SECTION_APPLICATION],
+            keys[CONFIG_MAINMODULE_KEY], FBootFields->FMainModule);
+
+    // Classpath.
+    // 1. If the provided class path contains main jar then only use
+    //    provided class path.
+    // 2. If class path provided by config file is empty then add main jar.
+    // 3. If main jar is not in provided class path then add it.
+    config->GetValue(keys[CONFIG_SECTION_APPLICATION],
+            keys[CONFIG_CLASSPATH_KEY], FBootFields->FClassPath);
+    FBootFields->FClassPath =
+            FilePath::FixPathSeparatorForPlatform(FBootFields->FClassPath);
+
+    if (FBootFields->FClassPath.empty() == true) {
+        FBootFields->FClassPath = GetMainJar();
+    } else if (FBootFields->FClassPath.find(GetMainJar()) == TString::npos) {
+        FBootFields->FClassPath = GetMainJar()
+                + FilePath::PathSeparator() + FBootFields->FClassPath;
+    }
+
+    // Modulepath.
+    config->GetValue(keys[CONFIG_SECTION_APPLICATION],
+            keys[CONFIG_MODULEPATH_KEY], FBootFields->FModulePath);
+    FBootFields->FModulePath =
+            FilePath::FixPathSeparatorForPlatform(FBootFields->FModulePath);
+
+    // Main Class.
+    config->GetValue(keys[CONFIG_SECTION_APPLICATION],
+            keys[CONFIG_MAINCLASSNAME_KEY], FBootFields->FMainClassName);
+
+    // Splash Screen.
+    if (config->GetValue(keys[CONFIG_SECTION_APPLICATION],
+            keys[CONFIG_SPLASH_KEY],
+            FBootFields->FSplashScreenFileName) == true) {
+        FBootFields->FSplashScreenFileName =
+            FilePath::IncludeTrailingSeparator(GetPackageAppDirectory())
+            + FilePath::FixPathForPlatform(FBootFields->FSplashScreenFileName);
+
+        if (FilePath::FileExists(FBootFields->FSplashScreenFileName) == false) {
+            FBootFields->FSplashScreenFileName = _T("");
+        }
+    }
+
+    // Runtime.
+    config->GetValue(keys[CONFIG_SECTION_APPLICATION],
+            keys[JVM_RUNTIME_KEY], FBootFields->FJVMRuntimeDirectory);
+
+    // Read jvmargs.
+    PromoteAppCDSState(config);
+    ReadJVMArgs(config);
+
+    // Read args if none were passed in.
+    if (FBootFields->FArgs.size() == 0) {
+        OrderedMap<TString, TString> args;
+
+        if (config->GetSection(keys[CONFIG_SECTION_ARGOPTIONS], args) == true) {
+            FBootFields->FArgs = Helpers::MapToNameValueList(args);
+        }
+    }
+
+    // Auto Memory.
+    TString autoMemory;
+
+    if (config->GetValue(keys[CONFIG_SECTION_APPLICATION],
+            keys[CONFIG_APP_MEMORY], autoMemory) == true) {
+        if (autoMemory == _T("auto") || autoMemory == _T("100%")) {
+            FBootFields->FMemoryState = PackageBootFields::msAuto;
+            FBootFields->FMemorySize = platform.GetMemorySize();
+        } else if (autoMemory.length() == 2 && isdigit(autoMemory[0]) &&
+                autoMemory[1] == '%') {
+            FBootFields->FMemoryState = PackageBootFields::msAuto;
+            FBootFields->FMemorySize =
+                    StringToPercentageOfNumber(autoMemory.substr(0, 1),
+                    platform.GetMemorySize());
+        } else if (autoMemory.length() == 3 && isdigit(autoMemory[0]) &&
+                isdigit(autoMemory[1]) && autoMemory[2] == '%') {
+            FBootFields->FMemoryState = PackageBootFields::msAuto;
+            FBootFields->FMemorySize =
+                    StringToPercentageOfNumber(autoMemory.substr(0, 2),
+                    platform.GetMemorySize());
+        } else {
+            FBootFields->FMemoryState = PackageBootFields::msManual;
+            FBootFields->FMemorySize = 0;
+        }
+    }
+
+    // Debug
+    TString debug;
+    if (config->GetValue(keys[CONFIG_SECTION_APPLICATION],
+            keys[CONFIG_APP_DEBUG], debug) == true) {
+        FBootFields->FArgs.push_back(debug);
+    }
+}
+
+void Package::Clear() {
+    FreeBootFields();
+    FInitialized = false;
+}
+
+// This is the only location that the AppCDS state should be modified except
+// by command line arguments provided by the user.
+//
+// The state of AppCDS is as follows:
+//
+// -> cdsUninitialized
+//    -> cdsGenCache If -Xappcds:generatecache
+//    -> cdsDisabled If -Xappcds:off
+//    -> cdsEnabled If "AppCDSJVMOptions" section is present
+//    -> cdsAuto If "AppCDSJVMOptions" section is present and
+//               app.appcds.cache=auto
+//    -> cdsDisabled Default
+//
+void Package::PromoteAppCDSState(ISectionalPropertyContainer* Config) {
+    Platform& platform = Platform::GetInstance();
+    std::map<TString, TString> keys = platform.GetKeys();
+
+    // The AppCDS state can change at this point.
+    switch (platform.GetAppCDSState()) {
+        case cdsEnabled:
+        case cdsAuto:
+        case cdsDisabled:
+        case cdsGenCache: {
+            // Do nothing.
+            break;
+        }
+
+        case cdsUninitialized: {
+            if (Config->ContainsSection(
+                    keys[CONFIG_SECTION_APPCDSJVMOPTIONS]) == true) {
+                // If the AppCDS section is present then enable AppCDS.
+                TString appCDSCacheValue;
+
+                // If running with AppCDS enabled, and the configuration has
+                // been setup so "auto" is enabled, then
+                // the launcher will attempt to generate the cache file
+                // automatically and run the application.
+                if (Config->GetValue(keys[CONFIG_SECTION_APPLICATION],
+                        _T("app.appcds.cache"), appCDSCacheValue) == true &&
+                    appCDSCacheValue == _T("auto")) {
+                    platform.SetAppCDSState(cdsAuto);
+                }
+                else {
+                    platform.SetAppCDSState(cdsEnabled);
+                }
+            } else {
+
+                platform.SetAppCDSState(cdsDisabled);
+            }
+        }
+    }
+}
+
+void Package::ReadJVMArgs(ISectionalPropertyContainer* Config) {
+    Platform& platform = Platform::GetInstance();
+    std::map<TString, TString> keys = platform.GetKeys();
+
+    // Evaluate based on the current AppCDS state.
+    switch (platform.GetAppCDSState()) {
+        case cdsUninitialized: {
+            throw Exception(_T("Internal Error"));
+        }
+
+        case cdsDisabled: {
+            Config->GetSection(keys[CONFIG_SECTION_JVMOPTIONS],
+                    FBootFields->FJVMArgs);
+            break;
+        }
+
+        case cdsGenCache: {
+            Config->GetSection(keys[
+                    CONFIG_SECTION_APPCDSGENERATECACHEJVMOPTIONS],
+                    FBootFields->FJVMArgs);
+            break;
+        }
+
+        case cdsAuto:
+        case cdsEnabled: {
+            if (Config->GetValue(keys[CONFIG_SECTION_APPCDSJVMOPTIONS],
+                    _T( "-XX:SharedArchiveFile"),
+                    FBootFields->FAppCDSCacheFileName) == true) {
+                // File names may contain the incorrect path separators.
+                // The cache file name must be corrected at this point.
+                if (FBootFields->FAppCDSCacheFileName.empty() == false) {
+                    IniFile* iniConfig = dynamic_cast<IniFile*>(Config);
+
+                    if (iniConfig != NULL) {
+                        FBootFields->FAppCDSCacheFileName =
+                                FilePath::FixPathForPlatform(
+                                FBootFields->FAppCDSCacheFileName);
+                        iniConfig->SetValue(keys[
+                                CONFIG_SECTION_APPCDSJVMOPTIONS],
+                                _T( "-XX:SharedArchiveFile"),
+                                FBootFields->FAppCDSCacheFileName);
+                    }
+                }
+
+                Config->GetSection(keys[CONFIG_SECTION_APPCDSJVMOPTIONS],
+                        FBootFields->FJVMArgs);
+            }
+
+            break;
+        }
+    }
+}
+
+void Package::SetCommandLineArguments(int argc, TCHAR* argv[]) {
+    if (argc > 0) {
+        std::list<TString> args;
+
+        // Prepare app arguments. Skip value at index 0 -
+        // this is path to executable.
+        FBootFields->FCommandName = argv[0];
+
+        // Path to executable is at 0 index so start at index 1.
+        for (int index = 1; index < argc; index++) {
+            TString arg = argv[index];
+
+#ifdef DEBUG
+            if (arg == _T("-debug")) {
+                FDebugging = dsNative;
+            }
+
+            if (arg == _T("-javadebug")) {
+                FDebugging = dsJava;
+            }
+#endif //DEBUG
+#ifdef MAC
+            if (arg.find(_T("-psn_"), 0) != TString::npos) {
+                Platform& platform = Platform::GetInstance();
+
+                if (platform.IsMainThread() == true) {
+#ifdef DEBUG
+                    printf("%s\n", arg.c_str());
+#endif //DEBUG
+                    continue;
+                }
+            }
+
+            if (arg == _T("-NSDocumentRevisionsDebugMode")) {
+                // Ignore -NSDocumentRevisionsDebugMode and
+                // the following YES/NO
+                index++;
+                continue;
+            }
+#endif //MAC
+
+            args.push_back(arg);
+        }
+
+        if (args.size() > 0) {
+            FBootFields->FArgs = args;
+        }
+    }
+}
+
+Package& Package::GetInstance() {
+    static Package instance;
+    // Guaranteed to be destroyed. Instantiated on first use.
+    return instance;
+}
+
+Package::~Package(void) {
+    FreeBootFields();
+}
+
+void Package::FreeBootFields() {
+    if (FBootFields != NULL) {
+        delete FBootFields;
+        FBootFields = NULL;
+    }
+}
+
+OrderedMap<TString, TString> Package::GetJVMArgs() {
+    return FBootFields->FJVMArgs;
+}
+
+std::vector<TString> GetKeysThatAreNotDuplicates(OrderedMap<TString,
+        TString> &Defaults, OrderedMap<TString, TString> &Overrides) {
+    std::vector<TString> result;
+    std::vector<TString> overrideKeys = Overrides.GetKeys();
+
+    for (size_t index = 0; index < overrideKeys.size(); index++) {
+        TString overridesKey = overrideKeys[index];
+        TString overridesValue;
+        TString defaultValue;
+
+        if ((Defaults.ContainsKey(overridesKey) == false) ||
+           (Defaults.GetValue(overridesKey, defaultValue) == true &&
+            Overrides.GetValue(overridesKey, overridesValue) == true &&
+            defaultValue != overridesValue)) {
+            result.push_back(overridesKey);
+        }
+    }
+
+    return result;
+}
+
+OrderedMap<TString, TString> CreateOrderedMapFromKeyList(OrderedMap<TString,
+        TString> &Map, std::vector<TString> &Keys) {
+    OrderedMap<TString, TString> result;
+
+    for (size_t index = 0; index < Keys.size(); index++) {
+        TString key = Keys[index];
+        TString value;
+
+        if (Map.GetValue(key, value) == true) {
+            result.Append(key, value);
+        }
+    }
+
+    return result;
+}
+
+std::vector<TString> GetKeysThatAreNotOverridesOfDefaultValues(
+        OrderedMap<TString, TString> &Defaults, OrderedMap<TString,
+        TString> &Overrides) {
+    std::vector<TString> result;
+    std::vector<TString> keys = Overrides.GetKeys();
+
+    for (unsigned int index = 0; index< keys.size(); index++) {
+        TString key = keys[index];
+
+        if (Defaults.ContainsKey(key) == true) {
+            try {
+                TString value = Overrides[key];
+                Defaults[key] = value;
+            }
+            catch (std::out_of_range) {
+            }
+        }
+        else {
+            result.push_back(key);
+        }
+    }
+
+    return result;
+}
+
+std::list<TString> Package::GetArgs() {
+    assert(FBootFields != NULL);
+    return FBootFields->FArgs;
+}
+
+TString Package::GetPackageRootDirectory() {
+    assert(FBootFields != NULL);
+    return FBootFields->FPackageRootDirectory;
+}
+
+TString Package::GetPackageAppDirectory() {
+    assert(FBootFields != NULL);
+    return FBootFields->FPackageAppDirectory;
+}
+
+TString Package::GetPackageLauncherDirectory() {
+    assert(FBootFields != NULL);
+    return FBootFields->FPackageLauncherDirectory;
+}
+
+TString Package::GetAppDataDirectory() {
+    assert(FBootFields != NULL);
+    return FBootFields->FAppDataDirectory;
+}
+
+TString Package::GetAppCDSCacheDirectory() {
+    if (FAppCDSCacheDirectory.empty()) {
+        Platform& platform = Platform::GetInstance();
+        FAppCDSCacheDirectory = FilePath::IncludeTrailingSeparator(
+                platform.GetAppDataDirectory())
+                + FilePath::IncludeTrailingSeparator(
+                GetPackageAppDataDirectory()) + _T("cache");
+
+        Macros& macros = Macros::GetInstance();
+        FAppCDSCacheDirectory = macros.ExpandMacros(FAppCDSCacheDirectory);
+        FAppCDSCacheDirectory =
+                FilePath::FixPathForPlatform(FAppCDSCacheDirectory);
+    }
+
+    return FAppCDSCacheDirectory;
+}
+
+TString Package::GetAppCDSCacheFileName() {
+    assert(FBootFields != NULL);
+
+    if (FBootFields->FAppCDSCacheFileName.empty() == false) {
+        Macros& macros = Macros::GetInstance();
+        FBootFields->FAppCDSCacheFileName =
+                macros.ExpandMacros(FBootFields->FAppCDSCacheFileName);
+        FBootFields->FAppCDSCacheFileName =
+                FilePath::FixPathForPlatform(FBootFields->FAppCDSCacheFileName);
+    }
+
+    return FBootFields->FAppCDSCacheFileName;
+}
+
+TString Package::GetAppID() {
+    assert(FBootFields != NULL);
+    return FBootFields->FAppID;
+}
+
+TString Package::GetPackageAppDataDirectory() {
+    assert(FBootFields != NULL);
+    return FBootFields->FPackageAppDataDirectory;
+}
+
+TString Package::GetClassPath() {
+    assert(FBootFields != NULL);
+    return FBootFields->FClassPath;
+}
+
+TString Package::GetModulePath() {
+    assert(FBootFields != NULL);
+    return FBootFields->FModulePath;
+}
+
+TString Package::GetMainJar() {
+    assert(FBootFields != NULL);
+    return FBootFields->FMainJar;
+}
+
+TString Package::GetMainModule() {
+    assert(FBootFields != NULL);
+    return FBootFields->FMainModule;
+}
+
+TString Package::GetMainClassName() {
+    assert(FBootFields != NULL);
+    return FBootFields->FMainClassName;
+}
+
+TString Package::GetJVMLibraryFileName() {
+    assert(FBootFields != NULL);
+
+    if (FBootFields->FJVMLibraryFileName.empty() == true) {
+        Platform& platform = Platform::GetInstance();
+        Macros& macros = Macros::GetInstance();
+        TString jvmRuntimePath = macros.ExpandMacros(GetJVMRuntimeDirectory());
+        FBootFields->FJVMLibraryFileName =
+                platform.GetBundledJVMLibraryFileName(jvmRuntimePath);
+    }
+
+    return FBootFields->FJVMLibraryFileName;
+}
+
+TString Package::GetJVMRuntimeDirectory() {
+    assert(FBootFields != NULL);
+    return FBootFields->FJVMRuntimeDirectory;
+}
+
+TString Package::GetSplashScreenFileName() {
+    assert(FBootFields != NULL);
+    return FBootFields->FSplashScreenFileName;
+}
+
+bool Package::HasSplashScreen() {
+    assert(FBootFields != NULL);
+    return FilePath::FileExists(FBootFields->FSplashScreenFileName);
+}
+
+TString Package::GetCommandName() {
+    assert(FBootFields != NULL);
+    return FBootFields->FCommandName;
+}
+
+TPlatformNumber Package::GetMemorySize() {
+    assert(FBootFields != NULL);
+    return FBootFields->FMemorySize;
+}
+
+PackageBootFields::MemoryState Package::GetMemoryState() {
+    assert(FBootFields != NULL);
+    return FBootFields->FMemoryState;
+}
+
+DebugState Package::Debugging() {
+    return FDebugging;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/native/libapplauncher/Package.h	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2014, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef PACKAGE_H
+#define PACKAGE_H
+
+
+#include "Platform.h"
+#include "PlatformString.h"
+#include "FilePath.h"
+#include "PropertyFile.h"
+
+#include <map>
+#include <list>
+
+class PackageBootFields {
+public:
+    enum MemoryState {msManual, msAuto};
+
+public:
+    OrderedMap<TString, TString> FJVMArgs;
+    std::list<TString> FArgs;
+
+    TString FPackageRootDirectory;
+    TString FPackageAppDirectory;
+    TString FPackageLauncherDirectory;
+    TString FAppDataDirectory;
+    TString FAppID;
+    TString FPackageAppDataDirectory;
+    TString FClassPath;
+    TString FModulePath;
+    TString FMainJar;
+    TString FMainModule;
+    TString FMainClassName;
+    TString FJVMRuntimeDirectory;
+    TString FJVMLibraryFileName;
+    TString FSplashScreenFileName;
+    bool FUseJavaPreferences;
+    TString FCommandName;
+
+    TString FAppCDSCacheFileName;
+
+    TPlatformNumber FMemorySize;
+    MemoryState FMemoryState;
+};
+
+
+class Package {
+private:
+    Package(Package const&); // Don't Implement.
+    void operator=(Package const&); // Don't implement
+
+private:
+    bool FInitialized;
+    PackageBootFields* FBootFields;
+    TString FAppCDSCacheDirectory;
+
+    DebugState FDebugging;
+
+    Package(void);
+
+    TString GetMainJar();
+    void ReadJVMArgs(ISectionalPropertyContainer* Config);
+    void PromoteAppCDSState(ISectionalPropertyContainer* Config);
+
+public:
+    static Package& GetInstance();
+    ~Package(void);
+
+    void Initialize();
+    void Clear();
+    void FreeBootFields();
+
+    void SetCommandLineArguments(int argc, TCHAR* argv[]);
+
+    OrderedMap<TString, TString> GetJVMArgs();
+    TString GetMainModule();
+
+    std::list<TString> GetArgs();
+
+    TString GetPackageRootDirectory();
+    TString GetPackageAppDirectory();
+    TString GetPackageLauncherDirectory();
+    TString GetAppDataDirectory();
+
+    TString GetAppCDSCacheDirectory();
+    TString GetAppCDSCacheFileName();
+
+    TString GetAppID();
+    TString GetPackageAppDataDirectory();
+    TString GetClassPath();
+    TString GetModulePath();
+    TString GetMainClassName();
+    TString GetJVMLibraryFileName();
+    TString GetJVMRuntimeDirectory();
+    TString GetSplashScreenFileName();
+    bool HasSplashScreen();
+    TString GetCommandName();
+
+    TPlatformNumber GetMemorySize();
+    PackageBootFields::MemoryState GetMemoryState();
+
+    DebugState Debugging();
+};
+
+#endif // PACKAGE_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/native/libapplauncher/Platform.cpp	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 2014, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "Platform.h"
+#include "Messages.h"
+#include "PlatformString.h"
+#include "FilePath.h"
+
+#include <fstream>
+#include <locale>
+
+#ifdef WINDOWS
+#include "WindowsPlatform.h"
+#endif // WINDOWS
+#ifdef LINUX
+#include "LinuxPlatform.h"
+#endif // LINUX
+#ifdef MAC
+#include "MacPlatform.h"
+#endif // MAC
+
+Platform& Platform::GetInstance() {
+#ifdef WINDOWS
+    static WindowsPlatform instance;
+#endif // WINDOWS
+
+#ifdef LINUX
+    static LinuxPlatform instance;
+#endif // LINUX
+
+#ifdef MAC
+    static MacPlatform instance;
+#endif // MAC
+
+    return instance;
+}
+
+TString Platform::GetConfigFileName() {
+    TString result;
+    TString basedir = GetPackageAppDirectory();
+
+    if (basedir.empty() == false) {
+        basedir = FilePath::IncludeTrailingSeparator(basedir);
+        TString appConfig = basedir + GetAppName() + _T(".cfg");
+
+        if (FilePath::FileExists(appConfig) == true) {
+            result = appConfig;
+        }
+        else {
+            result = basedir + _T("package.cfg");
+
+            if (FilePath::FileExists(result) == false) {
+                result = _T("");
+            }
+        }
+    }
+
+    return result;
+}
+
+std::list<TString> Platform::LoadFromFile(TString FileName) {
+    std::list<TString> result;
+
+    if (FilePath::FileExists(FileName) == true) {
+        std::wifstream stream(FileName.data());
+        InitStreamLocale(&stream);
+
+        if (stream.is_open() == true) {
+            while (stream.eof() == false) {
+                std::wstring line;
+                std::getline(stream, line);
+
+                // # at the first character will comment out the line.
+                if (line.empty() == false && line[0] != '#') {
+                    result.push_back(PlatformString(line).toString());
+                }
+            }
+        }
+    }
+
+    return result;
+}
+
+void Platform::SaveToFile(TString FileName, std::list<TString> Contents, bool ownerOnly) {
+    TString path = FilePath::ExtractFilePath(FileName);
+
+    if (FilePath::DirectoryExists(path) == false) {
+        FilePath::CreateDirectory(path, ownerOnly);
+    }
+
+    std::wofstream stream(FileName.data());
+    InitStreamLocale(&stream);
+
+    FilePath::ChangePermissions(FileName.data(), ownerOnly);
+
+    if (stream.is_open() == true) {
+        for (std::list<TString>::const_iterator iterator =
+                Contents.begin(); iterator != Contents.end(); iterator++) {
+            TString line = *iterator;
+            stream << PlatformString(line).toUnicodeString() << std::endl;
+        }
+    }
+}
+
+std::map<TString, TString> Platform::GetKeys() {
+    std::map<TString, TString> keys;
+    keys.insert(std::map<TString, TString>::value_type(CONFIG_VERSION,
+            _T("app.version")));
+    keys.insert(std::map<TString, TString>::value_type(CONFIG_MAINJAR_KEY,
+            _T("app.mainjar")));
+    keys.insert(std::map<TString, TString>::value_type(CONFIG_MAINMODULE_KEY,
+            _T("app.mainmodule")));
+    keys.insert(std::map<TString, TString>::value_type(CONFIG_MAINCLASSNAME_KEY,
+            _T("app.mainclass")));
+    keys.insert(std::map<TString, TString>::value_type(CONFIG_CLASSPATH_KEY,
+            _T("app.classpath")));
+    keys.insert(std::map<TString, TString>::value_type(CONFIG_MODULEPATH_KEY,
+            _T("app.modulepath")));
+    keys.insert(std::map<TString, TString>::value_type(APP_NAME_KEY,
+            _T("app.name")));
+    keys.insert(std::map<TString, TString>::value_type(CONFIG_APP_ID_KEY,
+            _T("app.preferences.id")));
+    keys.insert(std::map<TString, TString>::value_type(JVM_RUNTIME_KEY,
+            _T("app.runtime")));
+    keys.insert(std::map<TString, TString>::value_type(JPACKAGE_APP_DATA_DIR,
+            _T("app.identifier")));
+    keys.insert(std::map<TString, TString>::value_type(CONFIG_SPLASH_KEY,
+            _T("app.splash")));
+    keys.insert(std::map<TString, TString>::value_type(CONFIG_APP_MEMORY,
+            _T("app.memory")));
+    keys.insert(std::map<TString, TString>::value_type(CONFIG_APP_DEBUG,
+            _T("app.debug")));
+    keys.insert(std::map<TString,
+            TString>::value_type(CONFIG_APPLICATION_INSTANCE,
+            _T("app.application.instance")));
+    keys.insert(std::map<TString,
+            TString>::value_type(CONFIG_SECTION_APPLICATION,
+            _T("Application")));
+    keys.insert(std::map<TString,
+            TString>::value_type(CONFIG_SECTION_JVMOPTIONS,
+            _T("JVMOptions")));
+    keys.insert(std::map<TString,
+            TString>::value_type(CONFIG_SECTION_APPCDSJVMOPTIONS,
+            _T("AppCDSJVMOptions")));
+    keys.insert(std::map<TString,
+            TString>::value_type(CONFIG_SECTION_APPCDSGENERATECACHEJVMOPTIONS,
+            _T("AppCDSGenerateCacheJVMOptions")));
+    keys.insert(std::map<TString,
+            TString>::value_type(CONFIG_SECTION_ARGOPTIONS,
+            _T("ArgOptions")));
+
+    return keys;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/native/libapplauncher/Platform.h	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,267 @@
+/*
+ * Copyright (c) 2014, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef PLATFORM_H
+#define PLATFORM_H
+
+#include "PlatformDefs.h"
+#include "Properties.h"
+#include "OrderedMap.h"
+#include "Library.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <memory.h>
+#include <string>
+#include <map>
+#include <list>
+#include <vector>
+#include <fstream>
+
+using namespace std;
+
+// Config file sections
+#define CONFIG_SECTION_APPLICATION       _T("CONFIG_SECTION_APPLICATION")
+#define CONFIG_SECTION_JVMOPTIONS        _T("CONFIG_SECTION_JVMOPTIONS")
+#define CONFIG_SECTION_APPCDSJVMOPTIONS  _T("CONFIG_SECTION_APPCDSJVMOPTIONS")
+#define CONFIG_SECTION_ARGOPTIONS        _T("CONFIG_SECTION_ARGOPTIONS")
+#define CONFIG_SECTION_APPCDSGENERATECACHEJVMOPTIONS \
+        _T("CONFIG_SECTION_APPCDSGENERATECACHEJVMOPTIONS")
+
+// Config file keys.
+#define CONFIG_VERSION            _T("CONFIG_VERSION")
+#define CONFIG_MAINJAR_KEY        _T("CONFIG_MAINJAR_KEY")
+#define CONFIG_MAINMODULE_KEY     _T("CONFIG_MAINMODULE_KEY")
+#define CONFIG_MAINCLASSNAME_KEY  _T("CONFIG_MAINCLASSNAME_KEY")
+#define CONFIG_CLASSPATH_KEY      _T("CONFIG_CLASSPATH_KEY")
+#define CONFIG_MODULEPATH_KEY     _T("CONFIG_MODULEPATH_KEY")
+#define APP_NAME_KEY              _T("APP_NAME_KEY")
+#define CONFIG_SPLASH_KEY         _T("CONFIG_SPLASH_KEY")
+#define CONFIG_APP_ID_KEY         _T("CONFIG_APP_ID_KEY")
+#define CONFIG_APP_MEMORY         _T("CONFIG_APP_MEMORY")
+#define CONFIG_APP_DEBUG          _T("CONFIG_APP_DEBUG")
+#define CONFIG_APPLICATION_INSTANCE _T("CONFIG_APPLICATION_INSTANCE")
+
+#define JVM_RUNTIME_KEY           _T("JVM_RUNTIME_KEY")
+#define JPACKAGE_APP_DATA_DIR     _T("CONFIG_APP_IDENTIFIER")
+
+struct WideString {
+    size_t length;
+    wchar_t* data;
+
+    WideString() { length = 0; data = NULL; }
+};
+
+struct MultibyteString {
+    size_t length;
+    char* data;
+
+    MultibyteString() { length = 0; data = NULL; }
+};
+
+class Process {
+protected:
+    std::list<TString> FOutput;
+
+public:
+    Process() {
+        Output.SetInstance(this);
+        Input.SetInstance(this);
+    }
+
+    virtual ~Process() {}
+
+    virtual bool IsRunning() = 0;
+    virtual bool Terminate() = 0;
+    virtual bool Execute(const TString Application,
+        const std::vector<TString> Arguments, bool AWait = false) = 0;
+    virtual bool Wait() = 0;
+    virtual TProcessID GetProcessID() = 0;
+
+    virtual std::list<TString> GetOutput() { return FOutput; }
+    virtual void SetInput(TString Value) = 0;
+
+    ReadProperty<Process, std::list<TString>, &Process::GetOutput> Output;
+    WriteProperty<Process, TString, &Process::SetInput> Input;
+};
+
+
+template <typename T>
+class AutoFreePtr {
+private:
+    T* FObject;
+
+public:
+    AutoFreePtr() {
+        FObject = NULL;
+    }
+
+    AutoFreePtr(T* Value) {
+        FObject = Value;
+    }
+
+    ~AutoFreePtr() {
+        if (FObject != NULL) {
+            delete FObject;
+        }
+    }
+
+    operator T* () const {
+        return FObject;
+    }
+
+    T& operator* () const {
+        return *FObject;
+    }
+
+    T* operator->() const {
+        return FObject;
+    }
+
+    T** operator&() {
+        return &FObject;
+    }
+
+    T* operator=(const T * rhs) {
+        FObject = rhs;
+        return FObject;
+    }
+};
+
+enum DebugState {dsNone, dsNative, dsJava};
+enum MessageResponse {mrOK, mrCancel};
+enum AppCDSState {cdsUninitialized, cdsDisabled,
+        cdsEnabled, cdsAuto, cdsGenCache};
+
+class Platform {
+private:
+    AppCDSState FAppCDSState;
+
+protected:
+    Platform(void): FAppCDSState(cdsUninitialized) {
+    }
+
+public:
+    AppCDSState GetAppCDSState() { return FAppCDSState; }
+    void SetAppCDSState(AppCDSState Value) { FAppCDSState = Value; }
+
+    static Platform& GetInstance();
+
+    virtual ~Platform(void) {}
+
+public:
+    virtual void ShowMessage(TString title, TString description) = 0;
+    virtual void ShowMessage(TString description) = 0;
+    virtual MessageResponse ShowResponseMessage(TString title,
+           TString description) = 0;
+
+    virtual void SetCurrentDirectory(TString Value) = 0;
+
+    // Caller must free result using delete[].
+    virtual TCHAR* ConvertStringToFileSystemString(TCHAR* Source,
+            bool &release) = 0;
+
+    // Caller must free result using delete[].
+    virtual TCHAR* ConvertFileSystemStringToString(TCHAR* Source,
+            bool &release) = 0;
+
+    // Returns:
+    // Windows=C:\Users\<username>\AppData\Local
+    // Linux=~/.local
+    // Mac=~/Library/Application Support
+    virtual TString GetAppDataDirectory() = 0;
+
+    virtual TString GetPackageAppDirectory() = 0;
+    virtual TString GetPackageLauncherDirectory() = 0;
+    virtual TString GetPackageRuntimeBinDirectory() = 0;
+    virtual TString GetAppName() = 0;
+
+    virtual TString GetConfigFileName();
+
+    virtual TString GetBundledJVMLibraryFileName(TString RuntimePath) = 0;
+
+    // Caller must free result.
+    virtual ISectionalPropertyContainer* GetConfigFile(TString FileName) = 0;
+
+    virtual TString GetModuleFileName() = 0;
+    virtual TString GetPackageRootDirectory() = 0;
+
+    virtual Module LoadLibrary(TString FileName) = 0;
+    virtual void FreeLibrary(Module Module) = 0;
+    virtual Procedure GetProcAddress(Module Module, std::string MethodName) = 0;
+
+    // Caller must free result.
+    virtual Process* CreateProcess() = 0;
+
+    virtual bool IsMainThread() = 0;
+
+    // Returns megabytes.
+    virtual TPlatformNumber GetMemorySize() = 0;
+
+    virtual std::map<TString, TString> GetKeys();
+
+    virtual void InitStreamLocale(wios *stream) = 0;
+    virtual std::list<TString> LoadFromFile(TString FileName);
+    virtual void SaveToFile(TString FileName,
+             std::list<TString> Contents, bool ownerOnly);
+
+    virtual TString GetTempDirectory() = 0;
+
+    virtual void addPlatformDependencies(JavaLibrary *pJavaLibrary) = 0;
+
+public:
+    // String helpers
+    // Caller must free result using delete[].
+    static void CopyString(char *Destination,
+            size_t NumberOfElements, const char *Source);
+
+    // Caller must free result using delete[].
+    static void CopyString(wchar_t *Destination,
+            size_t NumberOfElements, const wchar_t *Source);
+
+    static WideString MultibyteStringToWideString(const char* value);
+    static MultibyteString WideStringToMultibyteString(const wchar_t* value);
+};
+
+class Exception: public std::exception {
+private:
+    TString FMessage;
+
+protected:
+    void SetMessage(const TString Message) {
+        FMessage = Message;
+    }
+
+public:
+    explicit Exception() : exception() {}
+    explicit Exception(const TString Message) : exception() {
+        SetMessage(Message);
+    }
+    virtual ~Exception() throw() {}
+
+    TString GetMessage() { return FMessage; }
+};
+
+#endif // PLATFORM_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/native/libapplauncher/PlatformString.cpp	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,227 @@
+/*
+ * Copyright (c) 2014, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "PlatformString.h"
+
+#include "Helpers.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdlib.h>
+#include <memory.h>
+#include <sstream>
+#include <string.h>
+
+#include "jni.h"
+
+void PlatformString::initialize() {
+    FWideTStringToFree = NULL;
+    FLength = 0;
+    FData = NULL;
+}
+
+PlatformString::PlatformString(void) {
+    initialize();
+}
+
+PlatformString::~PlatformString(void) {
+    if (FData != NULL) {
+        delete[] FData;
+    }
+
+    if (FWideTStringToFree != NULL) {
+        delete[] FWideTStringToFree;
+    }
+}
+
+PlatformString::PlatformString(const PlatformString &value) {
+    initialize();
+    FLength = value.FLength;
+    FData = new char[FLength + 1];
+    Platform::CopyString(FData, FLength + 1, value.FData);
+}
+
+PlatformString::PlatformString(const char* value) {
+    initialize();
+    FLength = strlen(value);
+    FData = new char[FLength + 1];
+    Platform::CopyString(FData, FLength + 1, value);
+}
+
+PlatformString::PlatformString(size_t Value) {
+    initialize();
+
+    std::stringstream ss;
+    std::string s;
+    ss << Value;
+    s = ss.str();
+
+    FLength = strlen(s.c_str());
+    FData = new char[FLength + 1];
+    Platform::CopyString(FData, FLength + 1, s.c_str());
+}
+
+PlatformString::PlatformString(const wchar_t* value) {
+    initialize();
+    MultibyteString temp = Platform::WideStringToMultibyteString(value);
+    FLength = temp.length;
+    FData = temp.data;
+}
+
+PlatformString::PlatformString(const std::string &value) {
+    initialize();
+    const char* lvalue = value.data();
+    FLength = value.size();
+    FData = new char[FLength + 1];
+    Platform::CopyString(FData, FLength + 1, lvalue);
+}
+
+PlatformString::PlatformString(const std::wstring &value) {
+    initialize();
+    const wchar_t* lvalue = value.data();
+    MultibyteString temp = Platform::WideStringToMultibyteString(lvalue);
+    FLength = temp.length;
+    FData = temp.data;
+}
+
+TString PlatformString::Format(const TString value, ...) {
+    TString result = value;
+
+    va_list arglist;
+    va_start(arglist, value);
+
+    while (1) {
+        size_t pos = result.find(_T("%s"), 0);
+
+        if (pos == TString::npos) {
+            break;
+        }
+        else {
+            TCHAR* arg = va_arg(arglist, TCHAR*);
+
+            if (arg == NULL) {
+                break;
+            }
+            else {
+                result.replace(pos, StringLength(_T("%s")), arg);
+            }
+        }
+    }
+
+    va_end(arglist);
+
+    return result;
+}
+
+size_t PlatformString::length() {
+    return FLength;
+}
+
+char* PlatformString::c_str() {
+    return FData;
+}
+
+char* PlatformString::toMultibyte() {
+    return FData;
+}
+
+wchar_t* PlatformString::toWideString() {
+    WideString result = Platform::MultibyteStringToWideString(FData);
+
+    if (result.data != NULL) {
+        if (FWideTStringToFree != NULL) {
+            delete [] FWideTStringToFree;
+        }
+
+        FWideTStringToFree = result.data;
+    }
+
+    return result.data;
+}
+
+std::wstring PlatformString::toUnicodeString() {
+    std::wstring result;
+    wchar_t* data = toWideString();
+
+    if (FLength != 0 && data != NULL) {
+        // NOTE: Cleanup of result is handled by PlatformString destructor.
+        result = data;
+    }
+
+    return result;
+}
+
+std::string PlatformString::toStdString() {
+    std::string result;
+    char* data = toMultibyte();
+
+    if (FLength > 0 && data != NULL) {
+        result = data;
+    }
+
+    return result;
+}
+
+TCHAR* PlatformString::toPlatformString() {
+#ifdef _UNICODE
+    return toWideString();
+#else
+    return c_str();
+#endif //_UNICODE
+}
+
+TString PlatformString::toString() {
+#ifdef _UNICODE
+    return toUnicodeString();
+#else
+    return toStdString();
+#endif //_UNICODE
+}
+
+PlatformString::operator char* () {
+    return c_str();
+}
+
+PlatformString::operator wchar_t* () {
+    return toWideString();
+}
+
+PlatformString::operator std::wstring () {
+    return toUnicodeString();
+}
+
+char* PlatformString::duplicate(const char* Value) {
+    size_t length = strlen(Value);
+    char* result = new char[length + 1];
+    Platform::CopyString(result, length + 1, Value);
+    return result;
+}
+
+wchar_t* PlatformString::duplicate(const wchar_t* Value) {
+    size_t length = wcslen(Value);
+    wchar_t* result = new wchar_t[length + 1];
+    Platform::CopyString(result, length + 1, Value);
+    return result;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/native/libapplauncher/PlatformString.h	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2014, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef PLATFORMSTRING_H
+#define PLATFORMSTRING_H
+
+
+#include <string>
+#include <list>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "jni.h"
+#include "Platform.h"
+
+
+template <typename T>
+class DynamicBuffer {
+private:
+    T* FData;
+    size_t FSize;
+
+public:
+    DynamicBuffer(size_t Size) {
+        FSize = 0;
+        FData = NULL;
+        Resize(Size);
+    }
+
+    ~DynamicBuffer() {
+        delete[] FData;
+    }
+
+    T* GetData() { return FData; }
+    size_t GetSize() { return FSize; }
+
+    bool Resize(size_t Size) {
+        FSize = Size;
+
+        if (FData != NULL) {
+            delete[] FData;
+            FData = NULL;
+        }
+
+        if (FSize != 0) {
+            FData = new T[FSize];
+            if (FData != NULL) {
+                Zero();
+            } else {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    void Zero() {
+        memset(FData, 0, FSize * sizeof(T));
+    }
+
+    T& operator[](size_t index) {
+        return FData[index];
+    }
+};
+
+class PlatformString {
+private:
+    char* FData; // Stored as UTF-8
+    size_t FLength;
+    wchar_t* FWideTStringToFree;
+
+    void initialize();
+
+// Prohibit Heap-Based PlatformStrings
+private:
+    static void *operator new(size_t size);
+    static void operator delete(void *ptr);
+
+public:
+    PlatformString(void);
+    PlatformString(const PlatformString &value);
+    PlatformString(const char* value);
+    PlatformString(const wchar_t* value);
+    PlatformString(const std::string &value);
+    PlatformString(const std::wstring &value);
+    PlatformString(size_t Value);
+
+    static TString Format(const TString value, ...);
+
+    ~PlatformString(void);
+
+    size_t length();
+
+    char* c_str();
+    char* toMultibyte();
+    wchar_t* toWideString();
+    std::wstring toUnicodeString();
+    std::string toStdString();
+    TCHAR* toPlatformString();
+    TString toString();
+
+    operator char* ();
+    operator wchar_t* ();
+    operator std::wstring ();
+
+    // Caller must free result using delete[].
+    static char* duplicate(const char* Value);
+
+    // Caller must free result using delete[].
+    static wchar_t* duplicate(const wchar_t* Value);
+};
+
+
+#endif // PLATFORMSTRING_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/native/libapplauncher/Properties.h	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) 2014, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef PROPERTIES_H
+#define PROPERTIES_H
+
+#include "PlatformDefs.h"
+#include "OrderedMap.h"
+
+//#include <stdio.h>
+//#include <stdlib.h>
+//#include <memory.h>
+//#include <string>
+//#include <map>
+//#include <list>
+//#include <vector>
+//#include <fstream>
+
+//using namespace std;
+
+template <typename ObjectType, typename ValueType,
+        ValueType (ObjectType::*getter)(void),
+        void (ObjectType::*setter)(ValueType)>
+class Property {
+private:
+    ObjectType* FObject;
+
+public:
+    Property() {
+        FObject = NULL;
+    }
+
+    void SetInstance(ObjectType* Value) {
+        FObject = Value;
+    }
+
+    // To set the value using the set method.
+    ValueType operator =(const ValueType& Value) {
+        assert(FObject != NULL);
+        (FObject->*setter)(Value);
+        return Value;
+    }
+
+    // The Property class is treated as the internal type.
+    operator ValueType() {
+        assert(FObject != NULL);
+        return (FObject->*getter)();
+    }
+};
+
+template <typename ObjectType, typename ValueType,
+        ValueType (ObjectType::*getter)(void)>
+class ReadProperty {
+private:
+    ObjectType* FObject;
+
+public:
+    ReadProperty() {
+        FObject = NULL;
+    }
+
+    void SetInstance(ObjectType* Value) {
+        FObject = Value;
+    }
+
+    // The Property class is treated as the internal type.
+    operator ValueType() {
+        assert(FObject != NULL);
+        return (FObject->*getter)();
+    }
+};
+
+template <typename ObjectType, typename ValueType,
+        void (ObjectType::*setter)(ValueType)>
+class WriteProperty {
+private:
+    ObjectType* FObject;
+
+public:
+    WriteProperty() {
+        FObject = NULL;
+    }
+
+    void SetInstance(ObjectType* Value) {
+        FObject = Value;
+    }
+
+    // To set the value using the set method.
+    ValueType operator =(const ValueType& Value) {
+        assert(FObject != NULL);
+        (FObject->*setter)(Value);
+        return Value;
+    }
+};
+
+template <typename ValueType,
+        ValueType (*getter)(void), void (*setter)(ValueType)>
+class StaticProperty {
+public:
+    StaticProperty() {
+    }
+
+    // To set the value using the set method.
+    ValueType operator =(const ValueType& Value) {
+        (*getter)(Value);
+        return Value;
+    }
+
+    // The Property class is treated as the internal type which is the getter.
+    operator ValueType() {
+        return (*setter)();
+    }
+};
+
+template <typename ValueType, ValueType (*getter)(void)>
+class StaticReadProperty {
+public:
+    StaticReadProperty() {
+    }
+
+    // The Property class is treated as the internal type which is the getter.
+    operator ValueType() {
+        return (*getter)();
+    }
+};
+
+template <typename ValueType, void (*setter)(ValueType)>
+class StaticWriteProperty {
+public:
+    StaticWriteProperty() {
+    }
+
+    // To set the value using the set method.
+    ValueType operator =(const ValueType& Value) {
+        (*setter)(Value);
+        return Value;
+    }
+};
+
+class IPropertyContainer {
+public:
+    IPropertyContainer(void) {}
+    virtual ~IPropertyContainer(void) {}
+
+    virtual bool GetValue(const TString Key, TString& Value) = 0;
+    virtual size_t GetCount() = 0;
+};
+
+class ISectionalPropertyContainer {
+public:
+    ISectionalPropertyContainer(void) {}
+    virtual ~ISectionalPropertyContainer(void) {}
+
+    virtual bool GetValue(const TString SectionName,
+            const TString Key, TString& Value) = 0;
+    virtual bool ContainsSection(const TString SectionName) = 0;
+    virtual bool GetSection(const TString SectionName,
+            OrderedMap<TString, TString> &Data) = 0;
+};
+
+#endif // PROPERTIES_H
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/native/libapplauncher/PropertyFile.cpp	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2014, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "PropertyFile.h"
+
+#include "Helpers.h"
+#include "FilePath.h"
+
+#include <string>
+
+
+PropertyFile::PropertyFile(void) : IPropertyContainer() {
+    FReadOnly = false;
+    FModified = false;
+}
+
+PropertyFile::PropertyFile(const TString FileName) : IPropertyContainer() {
+    FReadOnly = true;
+    FModified = false;
+    LoadFromFile(FileName);
+}
+
+PropertyFile::PropertyFile(OrderedMap<TString, TString> Value) {
+    FData.Append(Value);
+}
+
+PropertyFile::PropertyFile(PropertyFile &Value) {
+    FData = Value.FData;
+    FReadOnly = Value.FReadOnly;
+    FModified = Value.FModified;
+}
+
+PropertyFile::~PropertyFile(void) {
+    FData.Clear();
+}
+
+void PropertyFile::SetModified(bool Value) {
+    FModified = Value;
+}
+
+bool PropertyFile::IsModified() {
+    return FModified;
+}
+
+bool PropertyFile::GetReadOnly() {
+    return FReadOnly;
+}
+
+void PropertyFile::SetReadOnly(bool Value) {
+    FReadOnly = Value;
+}
+
+bool PropertyFile::LoadFromFile(const TString FileName) {
+    bool result = false;
+    Platform& platform = Platform::GetInstance();
+
+    std::list<TString> contents = platform.LoadFromFile(FileName);
+
+    if (contents.empty() == false) {
+        for (std::list<TString>::const_iterator iterator = contents.begin();
+                iterator != contents.end(); iterator++) {
+            TString line = *iterator;
+            TString name;
+            TString value;
+
+            if (Helpers::SplitOptionIntoNameValue(line, name, value) == true) {
+                FData.Append(name, value);
+            }
+        }
+
+        SetModified(false);
+        result = true;
+    }
+
+    return result;
+}
+
+bool PropertyFile::SaveToFile(const TString FileName, bool ownerOnly) {
+    bool result = false;
+
+    if (GetReadOnly() == false && IsModified()) {
+        std::list<TString> contents;
+        std::vector<TString> keys = FData.GetKeys();
+
+        for (size_t index = 0; index < keys.size(); index++) {
+            TString name = keys[index];
+
+            try {
+                TString value;// = FData[index];
+
+                if (FData.GetValue(name, value) == true) {
+                    TString line = name + _T('=') + value;
+                    contents.push_back(line);
+                }
+            }
+            catch (std::out_of_range) {
+            }
+        }
+
+        Platform& platform = Platform::GetInstance();
+        platform.SaveToFile(FileName, contents, ownerOnly);
+
+        SetModified(false);
+        result = true;
+    }
+
+    return result;
+}
+
+bool PropertyFile::GetValue(const TString Key, TString& Value) {
+    return FData.GetValue(Key, Value);
+}
+
+bool PropertyFile::SetValue(const TString Key, TString Value) {
+    bool result = false;
+
+    if (GetReadOnly() == false) {
+        FData.SetValue(Key, Value);
+        SetModified(true);
+        result = true;
+    }
+
+    return result;
+}
+
+bool PropertyFile::RemoveKey(const TString Key) {
+    bool result = false;
+
+    if (GetReadOnly() == false) {
+        result = FData.RemoveByKey(Key);
+
+        if (result == true) {
+            SetModified(true);
+        }
+    }
+
+    return result;
+}
+
+size_t PropertyFile::GetCount() {
+    return FData.Count();
+}
+
+OrderedMap<TString, TString> PropertyFile::GetData() {
+    return FData;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/native/libapplauncher/PropertyFile.h	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2014, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef PROPERTYFILE_H
+#define PROPERTYFILE_H
+
+#include "Platform.h"
+#include "Helpers.h"
+
+
+class PropertyFile : public IPropertyContainer {
+private:
+    bool FReadOnly;
+    bool FModified;
+    OrderedMap<TString, TString> FData;
+
+    void SetModified(bool Value);
+
+public:
+    PropertyFile(void);
+    PropertyFile(const TString FileName);
+    PropertyFile(OrderedMap<TString, TString> Value);
+    PropertyFile(PropertyFile &Value);
+    virtual ~PropertyFile(void);
+
+    bool IsModified();
+    bool GetReadOnly();
+    void SetReadOnly(bool Value);
+
+    bool LoadFromFile(const TString FileName);
+    bool SaveToFile(const TString FileName, bool ownerOnly = true);
+
+    bool SetValue(const TString Key, TString Value);
+    bool RemoveKey(const TString Key);
+
+    OrderedMap<TString, TString> GetData();
+
+    // IPropertyContainer
+    virtual bool GetValue(const TString Key, TString& Value);
+    virtual size_t GetCount();
+};
+
+#endif // PROPERTYFILE_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/share/native/libapplauncher/main.cpp	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) 2014, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "Platform.h"
+#include "PlatformString.h"
+#include "FilePath.h"
+#include "PropertyFile.h"
+#include "JavaVirtualMachine.h"
+#include "Package.h"
+#include "Macros.h"
+#include "Messages.h"
+
+#include <stdio.h>
+#include <signal.h>
+#include <stdlib.h>
+
+/*
+This is the app launcher program for application packaging on Windows, Mac,
+    and Linux.
+
+Basic approach:
+  - Launcher (jpackageapplauncher) is executable that loads
+    applauncher.dll/libapplauncher.dylib/libapplauncher.so
+    and calls start_launcher below.
+  - Reads app/package.cfg or Info.plist or app/<appname>.cfg for application
+    launch configuration (package.cfg is property file).
+  - Load JVM with requested JVM settings (bundled client JVM if availble,
+    server or installed JVM otherwise).
+  - Wait for JVM to exit and then exit from Main
+  - To debug application by passing command line argument.
+  - Application folder is added to the library path (so LoadLibrary()) works.
+
+Limitations and future work:
+  - Running Java code in primordial thread may cause problems
+    (example: can not use custom stack size).
+    Solution used by java launcher is to create a new thread to invoke JVM.
+    See CR 6316197 for more information.
+*/
+
+extern "C" {
+
+    JNIEXPORT bool start_launcher(int argc, TCHAR* argv[]) {
+        bool result = false;
+        bool parentProcess = true;
+
+        // Platform must be initialize first.
+        Platform& platform = Platform::GetInstance();
+
+        try {
+            for (int index = 0; index < argc; index++) {
+                TString argument = argv[index];
+
+                if (argument == _T("-Xappcds:generatecache")) {
+                    platform.SetAppCDSState(cdsGenCache);
+                }
+                else if (argument == _T("-Xappcds:off")) {
+                    platform.SetAppCDSState(cdsDisabled);
+                }
+                else if (argument == _T("-Xapp:child")) {
+                    parentProcess = false;
+                }
+            }
+
+            // Package must be initialized after Platform is fully initialized.
+            Package& package = Package::GetInstance();
+            Macros::Initialize();
+            package.SetCommandLineArguments(argc, argv);
+            platform.SetCurrentDirectory(package.GetPackageAppDirectory());
+
+            switch (platform.GetAppCDSState()) {
+                case cdsDisabled:
+                case cdsUninitialized:
+                case cdsEnabled: {
+                    break;
+                }
+
+                case cdsGenCache: {
+                    TString cacheDirectory = package.GetAppCDSCacheDirectory();
+
+                    if (FilePath::DirectoryExists(cacheDirectory) == false) {
+                        FilePath::CreateDirectory(cacheDirectory, true);
+                    } else {
+                        TString cacheFileName =
+                                package.GetAppCDSCacheFileName();
+                        if (FilePath::FileExists(cacheFileName) == true) {
+                            FilePath::DeleteFile(cacheFileName);
+                        }
+                    }
+
+                    break;
+                }
+
+                case cdsAuto: {
+                    TString cacheFileName = package.GetAppCDSCacheFileName();
+
+                    if (parentProcess == true &&
+                            FilePath::FileExists(cacheFileName) == false) {
+                        AutoFreePtr<Process> process = platform.CreateProcess();
+                        std::vector<TString> args;
+                        args.push_back(_T("-Xappcds:generatecache"));
+                        args.push_back(_T("-Xapp:child"));
+                        process->Execute(
+                                platform.GetModuleFileName(), args, true);
+
+                        if (FilePath::FileExists(cacheFileName) == false) {
+                            // Cache does not exist after trying to generate it,
+                            // so run without cache.
+                            platform.SetAppCDSState(cdsDisabled);
+                            package.Clear();
+                            package.Initialize();
+                        }
+                    }
+
+                    break;
+                }
+            }
+
+            // Validation
+            switch (platform.GetAppCDSState()) {
+                case cdsDisabled:
+                case cdsGenCache: {
+                    // Do nothing.
+                    break;
+                }
+
+                case cdsEnabled:
+                case cdsAuto: {
+                    TString cacheFileName =
+                            package.GetAppCDSCacheFileName();
+
+                    if (FilePath::FileExists(cacheFileName) == false) {
+                        Messages& messages = Messages::GetInstance();
+                        TString message = PlatformString::Format(
+                                messages.GetMessage(
+                                APPCDS_CACHE_FILE_NOT_FOUND),
+                                cacheFileName.data());
+                        throw Exception(message);
+                    }
+                    break;
+                }
+
+                case cdsUninitialized: {
+                    platform.ShowMessage(_T("Internal Error"));
+                    break;
+                }
+            }
+
+            // Run App
+            result = RunVM();
+        } catch (Exception &e) {
+            platform.ShowMessage(e.GetMessage());
+        }
+
+        return result;
+    }
+
+    JNIEXPORT void stop_launcher() {
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/unix/native/libapplauncher/FileAttribute.h	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef FILEATTRIBUTE_H
+#define FILEATTRIBUTE_H
+
+enum FileAttribute {
+    faBlockSpecial,
+    faCharacterSpecial,
+    faFIFOSpecial,
+    faNormal,
+    faDirectory,
+    faSymbolicLink,
+    faSocket,
+
+    // Owner
+    faReadOnly,
+    faWriteOnly,
+    faReadWrite,
+    faExecute,
+
+    // Group
+    faGroupReadOnly,
+    faGroupWriteOnly,
+    faGroupReadWrite,
+    faGroupExecute,
+
+    // Others
+    faOthersReadOnly,
+    faOthersWriteOnly,
+    faOthersReadWrite,
+    faOthersExecute,
+
+    faHidden
+};
+
+#endif // FILEATTRIBUTE_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/unix/native/libapplauncher/FileAttributes.cpp	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,331 @@
+/*
+ * Copyright (c) 2014, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "FileAttributes.h"
+
+#include <algorithm>
+#include <list>
+#include <sys/stat.h>
+
+FileAttributes::FileAttributes(const TString FileName, bool FollowLink) {
+    FFileName = FileName;
+    FFollowLink = FollowLink;
+    ReadAttributes();
+}
+
+bool FileAttributes::WriteAttributes() {
+    bool result = false;
+
+    mode_t attributes = 0;
+
+    for (std::vector<FileAttribute>::const_iterator iterator =
+            FAttributes.begin();
+            iterator != FAttributes.end(); iterator++) {
+        switch (*iterator) {
+            case faBlockSpecial:
+            {
+                attributes |= S_IFBLK;
+                break;
+            }
+            case faCharacterSpecial:
+            {
+                attributes |= S_IFCHR;
+                break;
+            }
+            case faFIFOSpecial:
+            {
+                attributes |= S_IFIFO;
+                break;
+            }
+            case faNormal:
+            {
+                attributes |= S_IFREG;
+                break;
+            }
+            case faDirectory:
+            {
+                attributes |= S_IFDIR;
+                break;
+            }
+            case faSymbolicLink:
+            {
+                attributes |= S_IFLNK;
+                break;
+            }
+            case faSocket:
+            {
+                attributes |= S_IFSOCK;
+                break;
+            }
+
+                // Owner
+            case faReadOnly:
+            {
+                attributes |= S_IRUSR;
+                break;
+            }
+            case faWriteOnly:
+            {
+                attributes |= S_IWUSR;
+                break;
+            }
+            case faReadWrite:
+            {
+                attributes |= S_IRUSR;
+                attributes |= S_IWUSR;
+                break;
+            }
+            case faExecute:
+            {
+                attributes |= S_IXUSR;
+                break;
+            }
+
+                // Group
+            case faGroupReadOnly:
+            {
+                attributes |= S_IRGRP;
+                break;
+            }
+            case faGroupWriteOnly:
+            {
+                attributes |= S_IWGRP;
+                break;
+            }
+            case faGroupReadWrite:
+            {
+                attributes |= S_IRGRP;
+                attributes |= S_IWGRP;
+                break;
+            }
+            case faGroupExecute:
+            {
+                attributes |= S_IXGRP;
+                break;
+            }
+
+                // Others
+            case faOthersReadOnly:
+            {
+                attributes |= S_IROTH;
+                break;
+            }
+            case faOthersWriteOnly:
+            {
+                attributes |= S_IWOTH;
+                break;
+            }
+            case faOthersReadWrite:
+            {
+                attributes |= S_IROTH;
+                attributes |= S_IWOTH;
+                break;
+            }
+            case faOthersExecute:
+            {
+                attributes |= S_IXOTH;
+                break;
+            }
+            default:
+                break;
+        }
+    }
+
+    if (chmod(FFileName.data(), attributes) == 0) {
+        result = true;
+    }
+
+    return result;
+}
+
+#define S_ISRUSR(m)    (((m) & S_IRWXU) == S_IRUSR)
+#define S_ISWUSR(m)    (((m) & S_IRWXU) == S_IWUSR)
+#define S_ISXUSR(m)    (((m) & S_IRWXU) == S_IXUSR)
+
+#define S_ISRGRP(m)    (((m) & S_IRWXG) == S_IRGRP)
+#define S_ISWGRP(m)    (((m) & S_IRWXG) == S_IWGRP)
+#define S_ISXGRP(m)    (((m) & S_IRWXG) == S_IXGRP)
+
+#define S_ISROTH(m)    (((m) & S_IRWXO) == S_IROTH)
+#define S_ISWOTH(m)    (((m) & S_IRWXO) == S_IWOTH)
+#define S_ISXOTH(m)    (((m) & S_IRWXO) == S_IXOTH)
+
+bool FileAttributes::ReadAttributes() {
+    bool result = false;
+
+    struct stat status;
+
+    if (stat(StringToFileSystemString(FFileName), &status) == 0) {
+        result = true;
+
+        if (S_ISBLK(status.st_mode) != 0) {
+            FAttributes.push_back(faBlockSpecial);
+        }
+        if (S_ISCHR(status.st_mode) != 0) {
+            FAttributes.push_back(faCharacterSpecial);
+        }
+        if (S_ISFIFO(status.st_mode) != 0) {
+            FAttributes.push_back(faFIFOSpecial);
+        }
+        if (S_ISREG(status.st_mode) != 0) {
+            FAttributes.push_back(faNormal);
+        }
+        if (S_ISDIR(status.st_mode) != 0) {
+            FAttributes.push_back(faDirectory);
+        }
+        if (S_ISLNK(status.st_mode) != 0) {
+            FAttributes.push_back(faSymbolicLink);
+        }
+        if (S_ISSOCK(status.st_mode) != 0) {
+            FAttributes.push_back(faSocket);
+        }
+
+        // Owner
+        if (S_ISRUSR(status.st_mode) != 0) {
+            if (S_ISWUSR(status.st_mode) != 0) {
+                FAttributes.push_back(faReadWrite);
+            } else {
+                FAttributes.push_back(faReadOnly);
+            }
+        } else if (S_ISWUSR(status.st_mode) != 0) {
+            FAttributes.push_back(faWriteOnly);
+        }
+
+        if (S_ISXUSR(status.st_mode) != 0) {
+            FAttributes.push_back(faExecute);
+        }
+
+        // Group
+        if (S_ISRGRP(status.st_mode) != 0) {
+            if (S_ISWGRP(status.st_mode) != 0) {
+                FAttributes.push_back(faGroupReadWrite);
+            } else {
+                FAttributes.push_back(faGroupReadOnly);
+            }
+        } else if (S_ISWGRP(status.st_mode) != 0) {
+            FAttributes.push_back(faGroupWriteOnly);
+        }
+
+        if (S_ISXGRP(status.st_mode) != 0) {
+            FAttributes.push_back(faGroupExecute);
+        }
+
+
+        // Others
+        if (S_ISROTH(status.st_mode) != 0) {
+            if (S_ISWOTH(status.st_mode) != 0) {
+                FAttributes.push_back(faOthersReadWrite);
+            } else {
+                FAttributes.push_back(faOthersReadOnly);
+            }
+        } else if (S_ISWOTH(status.st_mode) != 0) {
+            FAttributes.push_back(faOthersWriteOnly);
+        }
+
+        if (S_ISXOTH(status.st_mode) != 0) {
+            FAttributes.push_back(faOthersExecute);
+        }
+
+        if (FFileName.size() > 0 && FFileName[0] == '.') {
+            FAttributes.push_back(faHidden);
+        }
+    }
+
+    return result;
+}
+
+bool FileAttributes::Valid(const FileAttribute Value) {
+    bool result = false;
+
+    switch (Value) {
+        case faReadWrite:
+        case faWriteOnly:
+        case faExecute:
+
+        case faGroupReadWrite:
+        case faGroupWriteOnly:
+        case faGroupReadOnly:
+        case faGroupExecute:
+
+        case faOthersReadWrite:
+        case faOthersWriteOnly:
+        case faOthersReadOnly:
+        case faOthersExecute:
+
+        case faReadOnly:
+            result = true;
+            break;
+
+        default:
+            break;
+    }
+
+    return result;
+}
+
+void FileAttributes::Append(FileAttribute Value) {
+    if (Valid(Value) == true) {
+        if ((Value == faReadOnly && Contains(faWriteOnly) == true) ||
+                (Value == faWriteOnly && Contains(faReadOnly) == true)) {
+            Value = faReadWrite;
+        }
+
+        FAttributes.push_back(Value);
+        WriteAttributes();
+    }
+}
+
+bool FileAttributes::Contains(FileAttribute Value) {
+    bool result = false;
+
+    std::vector<FileAttribute>::const_iterator iterator =
+            std::find(FAttributes.begin(), FAttributes.end(), Value);
+
+    if (iterator != FAttributes.end()) {
+        result = true;
+    }
+
+    return result;
+}
+
+void FileAttributes::Remove(FileAttribute Value) {
+    if (Valid(Value) == true) {
+        if (Value == faReadOnly && Contains(faReadWrite) == true) {
+            Append(faWriteOnly);
+            Remove(faReadWrite);
+        } else if (Value == faWriteOnly && Contains(faReadWrite) == true) {
+            Append(faReadOnly);
+            Remove(faReadWrite);
+        }
+
+        std::vector<FileAttribute>::iterator iterator =
+                std::find(FAttributes.begin(), FAttributes.end(), Value);
+
+        if (iterator != FAttributes.end()) {
+            FAttributes.erase(iterator);
+            WriteAttributes();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/unix/native/libapplauncher/FilePath.cpp	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2014, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "PlatformDefs.h"
+#include "FilePath.h"
+
+#include <algorithm>
+#include <list>
+#include <sys/stat.h>
+
+bool FilePath::FileExists(const TString FileName) {
+    bool result = false;
+    struct stat buf;
+
+    if ((stat(StringToFileSystemString(FileName), &buf) == 0) &&
+            (S_ISREG(buf.st_mode) != 0)) {
+        result = true;
+    }
+
+    return result;
+}
+
+bool FilePath::DirectoryExists(const TString DirectoryName) {
+    bool result = false;
+
+    struct stat buf;
+
+    if ((stat(StringToFileSystemString(DirectoryName), &buf) == 0) &&
+            (S_ISDIR(buf.st_mode) != 0)) {
+        result = true;
+    }
+
+    return result;
+}
+
+bool FilePath::DeleteFile(const TString FileName) {
+    bool result = false;
+
+    if (FileExists(FileName) == true) {
+        if (unlink(StringToFileSystemString(FileName)) == 0) {
+            result = true;
+        }
+    }
+
+    return result;
+}
+
+bool FilePath::DeleteDirectory(const TString DirectoryName) {
+    bool result = false;
+
+    if (DirectoryExists(DirectoryName) == true) {
+        if (unlink(StringToFileSystemString(DirectoryName)) == 0) {
+            result = true;
+        }
+    }
+
+    return result;
+}
+
+TString FilePath::IncludeTrailingSeparator(const TString value) {
+    TString result = value;
+
+    if (value.size() > 0) {
+        TString::iterator i = result.end();
+        i--;
+
+        if (*i != TRAILING_PATHSEPARATOR) {
+            result += TRAILING_PATHSEPARATOR;
+        }
+    }
+
+    return result;
+}
+
+TString FilePath::IncludeTrailingSeparator(const char* value) {
+    TString lvalue = PlatformString(value).toString();
+    return IncludeTrailingSeparator(lvalue);
+}
+
+TString FilePath::IncludeTrailingSeparator(const wchar_t* value) {
+    TString lvalue = PlatformString(value).toString();
+    return IncludeTrailingSeparator(lvalue);
+}
+
+TString FilePath::ExtractFilePath(TString Path) {
+    return dirname(StringToFileSystemString(Path));
+}
+
+TString FilePath::ExtractFileExt(TString Path) {
+    TString result;
+    size_t dot = Path.find_last_of('.');
+
+    if (dot != TString::npos) {
+        result = Path.substr(dot, Path.size() - dot);
+    }
+
+    return result;
+}
+
+TString FilePath::ExtractFileName(TString Path) {
+    return basename(StringToFileSystemString(Path));
+}
+
+TString FilePath::ChangeFileExt(TString Path, TString Extension) {
+    TString result;
+    size_t dot = Path.find_last_of('.');
+
+    if (dot != TString::npos) {
+        result = Path.substr(0, dot) + Extension;
+    }
+
+    if (result.empty() == true) {
+        result = Path;
+    }
+
+    return result;
+}
+
+TString FilePath::FixPathForPlatform(TString Path) {
+    TString result = Path;
+    std::replace(result.begin(), result.end(),
+            BAD_TRAILING_PATHSEPARATOR, TRAILING_PATHSEPARATOR);
+    return result;
+}
+
+TString FilePath::FixPathSeparatorForPlatform(TString Path) {
+    TString result = Path;
+    std::replace(result.begin(), result.end(),
+            BAD_PATH_SEPARATOR, PATH_SEPARATOR);
+    return result;
+}
+
+TString FilePath::PathSeparator() {
+    TString result;
+    result = PATH_SEPARATOR;
+    return result;
+}
+
+bool FilePath::CreateDirectory(TString Path, bool ownerOnly) {
+    bool result = false;
+
+    std::list<TString> paths;
+    TString lpath = Path;
+
+    while (lpath.empty() == false && DirectoryExists(lpath) == false) {
+        paths.push_front(lpath);
+        lpath = ExtractFilePath(lpath);
+    }
+
+    for (std::list<TString>::iterator iterator = paths.begin();
+            iterator != paths.end(); iterator++) {
+        lpath = *iterator;
+
+        mode_t mode = S_IRWXU;
+        if (!ownerOnly) {
+            mode |= S_IRWXG | S_IROTH | S_IXOTH;
+        }
+        if (mkdir(StringToFileSystemString(lpath), mode) == 0) {
+            result = true;
+        } else {
+            result = false;
+            break;
+        }
+    }
+
+    return result;
+}
+
+void FilePath::ChangePermissions(TString FileName, bool ownerOnly) {
+    mode_t mode = S_IRWXU;
+    if (!ownerOnly) {
+        mode |= S_IRWXG | S_IROTH | S_IXOTH;
+    }
+    chmod(FileName.data(), mode);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/unix/native/libapplauncher/PosixPlatform.cpp	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,320 @@
+/*
+ * Copyright (c) 2014, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "PosixPlatform.h"
+
+#include "PlatformString.h"
+#include "FilePath.h"
+#include "Helpers.h"
+
+#include <assert.h>
+#include <stdbool.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/sysctl.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <limits.h>
+#include <pwd.h>
+#include <iostream>
+#include <algorithm>
+#include <dlfcn.h>
+#include <signal.h>
+
+using namespace std;
+
+PosixPlatform::PosixPlatform(void) {
+}
+
+PosixPlatform::~PosixPlatform(void) {
+}
+
+TString PosixPlatform::GetTempDirectory() {
+    struct passwd* pw = getpwuid(getuid());
+    TString homedir(pw->pw_dir);
+    homedir += getTmpDirString();
+    if (!FilePath::DirectoryExists(homedir)) {
+        if (!FilePath::CreateDirectory(homedir, false)) {
+            homedir.clear();
+        }
+    }
+
+    return homedir;
+}
+
+TString PosixPlatform::fixName(const TString& name) {
+    TString fixedName(name);
+    const TString chars("?:*<>/\\");
+    for (TString::const_iterator it = chars.begin(); it != chars.end(); it++) {
+        fixedName.erase(std::remove(fixedName.begin(),
+                fixedName.end(), *it), fixedName.end());
+    }
+    return fixedName;
+}
+
+MessageResponse PosixPlatform::ShowResponseMessage(TString title,
+        TString description) {
+    MessageResponse result = mrCancel;
+
+    printf("%s %s (Y/N)\n", PlatformString(title).toPlatformString(),
+            PlatformString(description).toPlatformString());
+    fflush(stdout);
+
+    std::string input;
+    std::cin >> input;
+
+    if (input == "Y") {
+        result = mrOK;
+    }
+
+    return result;
+}
+
+void PosixPlatform::SetCurrentDirectory(TString Value) {
+    chdir(StringToFileSystemString(Value));
+}
+
+Module PosixPlatform::LoadLibrary(TString FileName) {
+    return dlopen(StringToFileSystemString(FileName), RTLD_LAZY);
+}
+
+void PosixPlatform::FreeLibrary(Module AModule) {
+    dlclose(AModule);
+}
+
+Procedure PosixPlatform::GetProcAddress(Module AModule,
+        std::string MethodName) {
+    return dlsym(AModule, PlatformString(MethodName));
+}
+
+Process* PosixPlatform::CreateProcess() {
+    return new PosixProcess();
+}
+
+void PosixPlatform::addPlatformDependencies(JavaLibrary *pJavaLibrary) {
+}
+
+void Platform::CopyString(char *Destination,
+        size_t NumberOfElements, const char *Source) {
+    strncpy(Destination, Source, NumberOfElements);
+
+    if (NumberOfElements > 0) {
+        Destination[NumberOfElements - 1] = '\0';
+    }
+}
+
+void Platform::CopyString(wchar_t *Destination,
+        size_t NumberOfElements, const wchar_t *Source) {
+    wcsncpy(Destination, Source, NumberOfElements);
+
+    if (NumberOfElements > 0) {
+        Destination[NumberOfElements - 1] = '\0';
+    }
+}
+
+// Owner must free the return value.
+
+MultibyteString Platform::WideStringToMultibyteString(
+        const wchar_t* value) {
+    MultibyteString result;
+    size_t count = 0;
+
+    if (value == NULL) {
+        return result;
+    }
+
+    count = wcstombs(NULL, value, 0);
+    if (count > 0) {
+        result.data = new char[count + 1];
+        result.data[count] = '\0';
+        result.length = count;
+        wcstombs(result.data, value, count);
+    }
+
+    return result;
+}
+
+// Owner must free the return value.
+
+WideString Platform::MultibyteStringToWideString(const char* value) {
+    WideString result;
+    size_t count = 0;
+
+    if (value == NULL) {
+        return result;
+    }
+
+    count = mbstowcs(NULL, value, 0);
+    if (count > 0) {
+        result.data = new wchar_t[count + 1];
+        result.data[count] = '\0';
+        result.length = count;
+        mbstowcs(result.data, value, count);
+    }
+
+    return result;
+}
+
+void PosixPlatform::InitStreamLocale(wios *stream) {
+    // Nothing to do for POSIX platforms.
+}
+
+PosixProcess::PosixProcess() : Process() {
+    FChildPID = 0;
+    FRunning = false;
+    FOutputHandle = 0;
+    FInputHandle = 0;
+}
+
+PosixProcess::~PosixProcess() {
+    Terminate();
+}
+
+bool PosixProcess::ReadOutput() {
+    bool result = false;
+
+    if (FOutputHandle != 0 && IsRunning() == true) {
+        char buffer[4096] = {0};
+
+        ssize_t count = read(FOutputHandle, buffer, sizeof (buffer));
+
+        if (count == -1) {
+            if (errno == EINTR) {
+                // continue;
+            } else {
+                perror("read");
+                exit(1);
+            }
+        } else if (count == 0) {
+            // break;
+        } else {
+            if (buffer[count - 1] == EOF) {
+                buffer[count - 1] = '\0';
+            }
+
+            std::list<TString> output = Helpers::StringToArray(buffer);
+            FOutput.splice(FOutput.end(), output, output.begin(), output.end());
+            result = true;
+        }
+    }
+
+    return false;
+}
+
+bool PosixProcess::IsRunning() {
+    bool result = false;
+
+    if (kill(FChildPID, 0) == 0) {
+        result = true;
+    }
+
+    return result;
+}
+
+bool PosixProcess::Terminate() {
+    bool result = false;
+
+    if (IsRunning() == true && FRunning == true) {
+        FRunning = false;
+        Cleanup();
+        int status = kill(FChildPID, SIGTERM);
+
+        if (status == 0) {
+            result = true;
+        } else {
+#ifdef DEBUG
+            if (errno == EINVAL) {
+                printf("Kill error: The value of the sig argument is an invalid or unsupported signal number.");
+            } else if (errno == EPERM) {
+                printf("Kill error: The process does not have permission to send the signal to any receiving process.");
+            } else if (errno == ESRCH) {
+                printf("Kill error: No process or process group can be found corresponding to that specified by pid.");
+            }
+#endif // DEBUG
+            if (IsRunning() == true) {
+                status = kill(FChildPID, SIGKILL);
+
+                if (status == 0) {
+                    result = true;
+                }
+            }
+        }
+    }
+
+    return result;
+}
+
+bool PosixProcess::Wait() {
+    bool result = false;
+
+    int status = 0;
+    pid_t wpid = 0;
+
+    wpid = wait(&status);
+    if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+        if (errno != EINTR) {
+            status = -1;
+        }
+    }
+
+#ifdef DEBUG
+    if (WIFEXITED(status)) {
+        printf("child exited, status=%d\n", WEXITSTATUS(status));
+    } else if (WIFSIGNALED(status)) {
+        printf("child killed (signal %d)\n", WTERMSIG(status));
+    } else if (WIFSTOPPED(status)) {
+        printf("child stopped (signal %d)\n", WSTOPSIG(status));
+#ifdef WIFCONTINUED // Not all implementations support this
+    } else if (WIFCONTINUED(status)) {
+        printf("child continued\n");
+#endif // WIFCONTINUED
+    } else { // Non-standard case -- may never happen
+        printf("Unexpected status (0x%x)\n", status);
+    }
+#endif // DEBUG
+
+    if (wpid != -1) {
+        result = true;
+    }
+
+    return result;
+}
+
+TProcessID PosixProcess::GetProcessID() {
+    return FChildPID;
+}
+
+void PosixProcess::SetInput(TString Value) {
+    if (FInputHandle != 0) {
+        write(FInputHandle, Value.data(), Value.size());
+    }
+}
+
+std::list<TString> PosixProcess::GetOutput() {
+    ReadOutput();
+    return Process::GetOutput();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/unix/native/libapplauncher/PosixPlatform.h	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2014, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef POSIXPLATFORM_H
+#define POSIXPLATFORM_H
+
+#include "Platform.h"
+#include <signal.h>
+
+class PosixPlatform : virtual public Platform {
+protected:
+
+    TString fixName(const TString& name);
+
+    virtual TString getTmpDirString() = 0;
+
+public:
+    PosixPlatform(void);
+    virtual ~PosixPlatform(void);
+
+public:
+    virtual MessageResponse ShowResponseMessage(TString title,
+            TString description);
+
+    virtual void SetCurrentDirectory(TString Value);
+
+    virtual Module LoadLibrary(TString FileName);
+    virtual void FreeLibrary(Module AModule);
+    virtual Procedure GetProcAddress(Module AModule, std::string MethodName);
+
+    virtual Process* CreateProcess();
+    virtual TString GetTempDirectory();
+    void InitStreamLocale(wios *stream);
+    void addPlatformDependencies(JavaLibrary *pJavaLibrary);
+};
+
+class PosixProcess : public Process {
+private:
+    pid_t FChildPID;
+    sigset_t saveblock;
+    int FOutputHandle;
+    int FInputHandle;
+    struct sigaction savintr, savequit;
+    bool FRunning;
+
+    void Cleanup();
+    bool ReadOutput();
+
+public:
+    PosixProcess();
+    virtual ~PosixProcess();
+
+    virtual bool IsRunning();
+    virtual bool Terminate();
+    virtual bool Execute(const TString Application,
+            const std::vector<TString> Arguments, bool AWait = false);
+    virtual bool Wait();
+    virtual TProcessID GetProcessID();
+    virtual void SetInput(TString Value);
+    virtual std::list<TString> GetOutput();
+};
+
+#endif // POSIXPLATFORM_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinAppBundler.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,282 @@
+/*
+ * Copyright (c) 2012, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jpackage.internal;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.nio.file.Path;
+import java.text.MessageFormat;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Map;
+import java.util.ResourceBundle;
+
+import static jdk.jpackage.internal.WindowsBundlerParam.*;
+import static jdk.jpackage.internal.WinMsiBundler.WIN_APP_IMAGE;
+
+public class WinAppBundler extends AbstractImageBundler {
+
+    private static final ResourceBundle I18N = ResourceBundle.getBundle(
+            "jdk.jpackage.internal.resources.WinResources");
+
+    static final BundlerParamInfo<File> ICON_ICO =
+            new StandardBundlerParam<>(
+            I18N.getString("param.icon-ico.name"),
+            I18N.getString("param.icon-ico.description"),
+            "icon.ico",
+            File.class,
+            params -> {
+                File f = ICON.fetchFrom(params);
+                if (f != null && !f.getName().toLowerCase().endsWith(".ico")) {
+                    Log.error(MessageFormat.format(
+                            I18N.getString("message.icon-not-ico"), f));
+                    return null;
+                }
+                return f;
+            },
+            (s, p) -> new File(s));
+
+    @Override
+    public boolean validate(Map<String, ? super Object> params)
+            throws UnsupportedPlatformException, ConfigException {
+        try {
+            if (params == null) throw new ConfigException(
+                    I18N.getString("error.parameters-null"),
+                    I18N.getString("error.parameters-null.advice"));
+
+            return doValidate(params);
+        } catch (RuntimeException re) {
+            if (re.getCause() instanceof ConfigException) {
+                throw (ConfigException) re.getCause();
+            } else {
+                throw new ConfigException(re);
+            }
+        }
+    }
+
+    // to be used by chained bundlers, e.g. by EXE bundler to avoid
+    // skipping validation if p.type does not include "image"
+    private boolean doValidate(Map<String, ? super Object> p)
+            throws UnsupportedPlatformException, ConfigException {
+        if (Platform.getPlatform() != Platform.WINDOWS) {
+            throw new UnsupportedPlatformException();
+        }
+
+        imageBundleValidation(p);
+
+        if (StandardBundlerParam.getPredefinedAppImage(p) != null) {
+            return true;
+        }
+
+        // Make sure that jpackage.exe exists.
+        File tool = new File(
+                System.getProperty("java.home") + "\\bin\\jpackage.exe");
+
+        if (!tool.exists()) {
+            throw new ConfigException(
+                    I18N.getString("error.no-windows-resources"),
+                    I18N.getString("error.no-windows-resources.advice"));
+        }
+
+        return true;
+    }
+
+    private static boolean usePredefineAppName(Map<String, ? super Object> p) {
+        return (PREDEFINED_APP_IMAGE.fetchFrom(p) != null);
+    }
+
+    private static String appName;
+    synchronized static String getAppName(
+            Map<String, ? super Object> p) {
+        // If we building from predefined app image, then we should use names
+        // from image and not from CLI.
+        if (usePredefineAppName(p)) {
+            if (appName == null) {
+                // Use WIN_APP_IMAGE here, since we already copy pre-defined
+                // image to WIN_APP_IMAGE
+                File appImageDir = WIN_APP_IMAGE.fetchFrom(p);
+
+                File appDir = new File(appImageDir.toString() + "\\app");
+                File [] files = appDir.listFiles(
+                        (File dir, String name) -> name.endsWith(".cfg"));
+                if (files == null || files.length == 0) {
+                    String name = APP_NAME.fetchFrom(p);
+                    Path exePath = appImageDir.toPath().resolve(name + ".exe");
+                    Path icoPath = appImageDir.toPath().resolve(name + ".ico");
+                    if (exePath.toFile().exists() &&
+                            icoPath.toFile().exists()) {
+                        return name;
+                    } else {
+                        throw new RuntimeException(MessageFormat.format(
+                                I18N.getString("error.cannot-find-launcher"),
+                                appImageDir));
+                    }
+                } else {
+                    appName = files[0].getName();
+                    int index = appName.indexOf(".");
+                    if (index != -1) {
+                        appName = appName.substring(0, index);
+                    }
+                    if (files.length > 1) {
+                        Log.error(MessageFormat.format(I18N.getString(
+                                "message.multiple-launchers"), appName));
+                    }
+                }
+                return appName;
+            } else {
+                return appName;
+            }
+        }
+
+        return APP_NAME.fetchFrom(p);
+    }
+
+    public static String getLauncherName(Map<String, ? super Object> p) {
+        return getAppName(p) + ".exe";
+    }
+
+    public static String getLauncherCfgName(Map<String, ? super Object> p) {
+        return "app\\" + getAppName(p) +".cfg";
+    }
+
+    public boolean bundle(Map<String, ? super Object> p, File outputDirectory)
+            throws PackagerException {
+        return doBundle(p, outputDirectory, false) != null;
+    }
+
+    File doBundle(Map<String, ? super Object> p, File outputDirectory,
+            boolean dependentTask) throws PackagerException {
+        if (RUNTIME_INSTALLER.fetchFrom(p)) {
+            return doJreBundle(p, outputDirectory, dependentTask);
+        } else {
+            return doAppBundle(p, outputDirectory, dependentTask);
+        }
+    }
+
+    File doJreBundle(Map<String, ? super Object> p, File outputDirectory,
+            boolean dependentTask) throws PackagerException {
+        try {
+            File rootDirectory = createRoot(p, outputDirectory, dependentTask,
+                APP_NAME.fetchFrom(p), "windowsapp-image-builder");
+            AbstractAppImageBuilder appBuilder = new WindowsAppImageBuilder(
+                    APP_NAME.fetchFrom(p),
+                    outputDirectory.toPath());
+            File predefined = PREDEFINED_RUNTIME_IMAGE.fetchFrom(p);
+            if (predefined == null ) {
+                JLinkBundlerHelper.generateJre(p, appBuilder);
+            } else {
+                return predefined;
+            }
+            return rootDirectory;
+        } catch (PackagerException pe) {
+            throw pe;
+        } catch (Exception e) {
+            Log.verbose(e);
+            throw new PackagerException(e);
+        }
+    }
+
+    File doAppBundle(Map<String, ? super Object> p, File outputDirectory,
+            boolean dependentTask) throws PackagerException {
+        try {
+            File rootDirectory = createRoot(p, outputDirectory, dependentTask,
+                    APP_NAME.fetchFrom(p), "windowsapp-image-builder");
+            AbstractAppImageBuilder appBuilder =
+                    new WindowsAppImageBuilder(p, outputDirectory.toPath());
+            if (PREDEFINED_RUNTIME_IMAGE.fetchFrom(p) == null ) {
+                JLinkBundlerHelper.execute(p, appBuilder);
+            } else {
+                StandardBundlerParam.copyPredefinedRuntimeImage(p, appBuilder);
+            }
+            if (!dependentTask) {
+                Log.verbose(MessageFormat.format(
+                        I18N.getString("message.result-dir"),
+                        outputDirectory.getAbsolutePath()));
+            }
+            return rootDirectory;
+        } catch (PackagerException pe) {
+            throw pe;
+        } catch (Exception e) {
+            Log.verbose(e);
+            throw new PackagerException(e);
+        }
+    }
+
+    @Override
+    public String getName() {
+        return I18N.getString("app.bundler.name");
+    }
+
+    @Override
+    public String getDescription() {
+        return I18N.getString("app.bundler.description");
+    }
+
+    @Override
+    public String getID() {
+        return "windows.app";
+    }
+
+    @Override
+    public String getBundleType() {
+        return "IMAGE";
+    }
+
+    @Override
+    public Collection<BundlerParamInfo<?>> getBundleParameters() {
+        return getAppBundleParameters();
+    }
+
+    public static Collection<BundlerParamInfo<?>> getAppBundleParameters() {
+        return Arrays.asList(
+                APP_NAME,
+                APP_RESOURCES,
+                ARGUMENTS,
+                CLASSPATH,
+                ICON_ICO,
+                JVM_OPTIONS,
+                MAIN_CLASS,
+                MAIN_JAR,
+                PREFERENCES_ID,
+                VERSION,
+                VERBOSE
+            );
+    }
+
+    @Override
+    public File execute(Map<String, ? super Object> params,
+            File outputParentDir) throws PackagerException {
+        return doBundle(params, outputParentDir, false);
+    }
+
+    @Override
+    public boolean supported(boolean platformInstaller) {
+        return (Platform.getPlatform() == Platform.WINDOWS);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinExeBundler.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,893 @@
+/*
+ * Copyright (c) 2017, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jpackage.internal;
+
+import java.io.*;
+import java.nio.charset.Charset;
+import java.nio.file.Files;
+import java.text.MessageFormat;
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import static jdk.jpackage.internal.WindowsBundlerParam.*;
+
+public class WinExeBundler extends AbstractBundler {
+
+    private static final ResourceBundle I18N = ResourceBundle.getBundle(
+            "jdk.jpackage.internal.resources.WinResources");
+
+    public static final BundlerParamInfo<WinAppBundler> APP_BUNDLER =
+            new WindowsBundlerParam<>(
+            getString("param.exe-bundler.name"),
+            getString("param.exe-bundler.description"),
+            "win.app.bundler",
+            WinAppBundler.class,
+            params -> new WinAppBundler(),
+            null);
+
+    public static final BundlerParamInfo<File> EXE_IMAGE_DIR =
+            new WindowsBundlerParam<>(
+            getString("param.image-dir.name"),
+            getString("param.image-dir.description"),
+            "win.exe.imageDir",
+            File.class,
+            params -> {
+                File imagesRoot = IMAGES_ROOT.fetchFrom(params);
+                if (!imagesRoot.exists()) imagesRoot.mkdirs();
+                return new File(imagesRoot, "win-exe.image");
+            },
+            (s, p) -> null);
+
+    public static final BundlerParamInfo<File> WIN_APP_IMAGE =
+            new WindowsBundlerParam<>(
+            getString("param.app-dir.name"),
+            getString("param.app-dir.description"),
+            "win.app.image",
+            File.class,
+            null,
+            (s, p) -> null);
+
+    public static final BundlerParamInfo<UUID> UPGRADE_UUID =
+            new WindowsBundlerParam<>(
+            I18N.getString("param.upgrade-uuid.name"),
+            I18N.getString("param.upgrade-uuid.description"),
+            Arguments.CLIOptions.WIN_UPGRADE_UUID.getId(),
+            UUID.class,
+            params -> UUID.randomUUID(),
+            (s, p) -> UUID.fromString(s));
+
+    public static final StandardBundlerParam<Boolean> EXE_SYSTEM_WIDE  =
+            new StandardBundlerParam<>(
+            getString("param.system-wide.name"),
+            getString("param.system-wide.description"),
+            Arguments.CLIOptions.WIN_PER_USER_INSTALLATION.getId(),
+            Boolean.class,
+            params -> true, // default to system wide
+            (s, p) -> (s == null || "null".equalsIgnoreCase(s))? null
+                    : Boolean.valueOf(s)
+            );
+    public static final StandardBundlerParam<String> PRODUCT_VERSION =
+            new StandardBundlerParam<>(
+                    getString("param.product-version.name"),
+                    getString("param.product-version.description"),
+                    "win.msi.productVersion",
+                    String.class,
+                    VERSION::fetchFrom,
+                    (s, p) -> s
+            );
+
+    public static final StandardBundlerParam<Boolean> MENU_HINT =
+        new WindowsBundlerParam<>(
+                getString("param.menu-shortcut-hint.name"),
+                getString("param.menu-shortcut-hint.description"),
+                Arguments.CLIOptions.WIN_MENU_HINT.getId(),
+                Boolean.class,
+                params -> false,
+                (s, p) -> (s == null ||
+                        "null".equalsIgnoreCase(s))? true : Boolean.valueOf(s)
+        );
+
+    public static final StandardBundlerParam<Boolean> SHORTCUT_HINT =
+        new WindowsBundlerParam<>(
+                getString("param.desktop-shortcut-hint.name"),
+                getString("param.desktop-shortcut-hint.description"),
+                Arguments.CLIOptions.WIN_SHORTCUT_HINT.getId(),
+                Boolean.class,
+                params -> false,
+                (s, p) -> (s == null ||
+                       "null".equalsIgnoreCase(s))? false : Boolean.valueOf(s)
+        );
+
+    private final static String DEFAULT_EXE_PROJECT_TEMPLATE = "template.iss";
+    private final static String DEFAULT_JRE_EXE_TEMPLATE = "template.jre.iss";
+    private static final String TOOL_INNO_SETUP_COMPILER = "iscc.exe";
+
+    public static final BundlerParamInfo<String>
+            TOOL_INNO_SETUP_COMPILER_EXECUTABLE = new WindowsBundlerParam<>(
+            getString("param.iscc-path.name"),
+            getString("param.iscc-path.description"),
+            "win.exe.iscc.exe",
+            String.class,
+            params -> {
+                for (String dirString : (System.getenv("PATH")
+                        + ";C:\\Program Files (x86)\\Inno Setup 5;"
+                        + "C:\\Program Files\\Inno Setup 5").split(";")) {
+                    File f = new File(dirString.replace("\"", ""),
+                            TOOL_INNO_SETUP_COMPILER);
+                    if (f.isFile()) {
+                        return f.toString();
+                    }
+                }
+                return null;
+            },
+            null);
+
+    @Override
+    public String getName() {
+        return getString("exe.bundler.name");
+    }
+
+    @Override
+    public String getDescription() {
+        return getString("exe.bundler.description");
+    }
+
+    @Override
+    public String getID() {
+        return "exe";
+    }
+
+    @Override
+    public String getBundleType() {
+        return "INSTALLER";
+    }
+
+    @Override
+    public Collection<BundlerParamInfo<?>> getBundleParameters() {
+        Collection<BundlerParamInfo<?>> results = new LinkedHashSet<>();
+        results.addAll(WinAppBundler.getAppBundleParameters());
+        results.addAll(getExeBundleParameters());
+        return results;
+    }
+
+    public static Collection<BundlerParamInfo<?>> getExeBundleParameters() {
+        return Arrays.asList(
+                DESCRIPTION,
+                COPYRIGHT,
+                LICENSE_FILE,
+                MENU_GROUP,
+                MENU_HINT,
+                SHORTCUT_HINT,
+                EXE_SYSTEM_WIDE,
+                TITLE,
+                VENDOR,
+                INSTALLDIR_CHOOSER
+        );
+    }
+
+    @Override
+    public File execute(Map<String, ? super Object> p,
+            File outputParentDir) throws PackagerException {
+        return bundle(p, outputParentDir);
+    }
+
+    @Override
+    public boolean supported(boolean platformInstaller) {
+        return (Platform.getPlatform() == Platform.WINDOWS);
+    }
+
+    private static String findToolVersion(String toolName) {
+        try {
+            if (toolName == null || "".equals(toolName)) return null;
+
+            ProcessBuilder pb = new ProcessBuilder(
+                    toolName,
+                    "/?");
+            VersionExtractor ve =
+                    new VersionExtractor("Inno Setup (\\d+.?\\d*)");
+            IOUtils.exec(pb, Log.isDebug(), true, ve);
+            // not interested in the output
+            String version = ve.getVersion();
+            Log.verbose(MessageFormat.format(
+                    getString("message.tool-version"), toolName, version));
+            return version;
+        } catch (Exception e) {
+            if (Log.isDebug()) {
+                Log.verbose(e);
+            }
+            return null;
+        }
+    }
+
+    @Override
+    public boolean validate(Map<String, ? super Object> p)
+            throws UnsupportedPlatformException, ConfigException {
+        try {
+            if (p == null) throw new ConfigException(
+                      getString("error.parameters-null"),
+                      getString("error.parameters-null.advice"));
+
+            // run basic validation to ensure requirements are met
+            // we are not interested in return code, only possible exception
+            APP_BUNDLER.fetchFrom(p).validate(p);
+
+            // make sure some key values don't have newlines
+            for (BundlerParamInfo<String> pi : Arrays.asList(
+                    APP_NAME,
+                    COPYRIGHT,
+                    DESCRIPTION,
+                    MENU_GROUP,
+                    TITLE,
+                    VENDOR,
+                    VERSION)
+            ) {
+                String v = pi.fetchFrom(p);
+                if (v.contains("\n") | v.contains("\r")) {
+                    throw new ConfigException("Parmeter '" + pi.getID() +
+                            "' cannot contain a newline.",
+                            " Change the value of '" + pi.getID() +
+                            " so that it does not contain any newlines");
+                }
+            }
+
+            // exe bundlers trim the copyright to 100 characters,
+            // tell them this will happen
+            if (COPYRIGHT.fetchFrom(p).length() > 100) {
+                throw new ConfigException(
+                        getString("error.copyright-is-too-long"),
+                        getString("error.copyright-is-too-long.advice"));
+            }
+
+            String innoVersion = findToolVersion(
+                    TOOL_INNO_SETUP_COMPILER_EXECUTABLE.fetchFrom(p));
+
+            //Inno Setup 5+ is required
+            String minVersion = "5.0";
+
+            if (VersionExtractor.isLessThan(innoVersion, minVersion)) {
+                Log.error(MessageFormat.format(
+                        getString("message.tool-wrong-version"),
+                        TOOL_INNO_SETUP_COMPILER, innoVersion, minVersion));
+                throw new ConfigException(
+                        getString("error.iscc-not-found"),
+                        getString("error.iscc-not-found.advice"));
+            }
+
+            /********* validate bundle parameters *************/
+
+            // only one mime type per association, at least one file extension
+            List<Map<String, ? super Object>> associations =
+                    FILE_ASSOCIATIONS.fetchFrom(p);
+            if (associations != null) {
+                for (int i = 0; i < associations.size(); i++) {
+                    Map<String, ? super Object> assoc = associations.get(i);
+                    List<String> mimes = FA_CONTENT_TYPE.fetchFrom(assoc);
+                    if (mimes.size() > 1) {
+                        throw new ConfigException(MessageFormat.format(
+                                getString("error.too-many-content-"
+                                + "types-for-file-association"), i),
+                                getString("error.too-many-content-"
+                                + "types-for-file-association.advice"));
+                    }
+                }
+            }
+
+            return true;
+        } catch (RuntimeException re) {
+            if (re.getCause() instanceof ConfigException) {
+                throw (ConfigException) re.getCause();
+            } else {
+                throw new ConfigException(re);
+            }
+        }
+    }
+
+    private boolean prepareProto(Map<String, ? super Object> p)
+                throws PackagerException, IOException {
+        File appImage = StandardBundlerParam.getPredefinedAppImage(p);
+        File appDir = null;
+
+        // we either have an application image or need to build one
+        if (appImage != null) {
+            appDir = new File(
+                    EXE_IMAGE_DIR.fetchFrom(p), APP_NAME.fetchFrom(p));
+            // copy everything from appImage dir into appDir/name
+            IOUtils.copyRecursive(appImage.toPath(), appDir.toPath());
+        } else {
+            appDir = APP_BUNDLER.fetchFrom(p).doBundle(p,
+                    EXE_IMAGE_DIR.fetchFrom(p), true);
+        }
+
+        if (appDir == null) {
+            return false;
+        }
+
+        p.put(WIN_APP_IMAGE.getID(), appDir);
+
+        String licenseFile = LICENSE_FILE.fetchFrom(p);
+        if (licenseFile != null) {
+            // need to copy license file to the working directory and convert to rtf if needed
+            File lfile = new File(licenseFile);
+            File destFile = new File(CONFIG_ROOT.fetchFrom(p), lfile.getName());
+            IOUtils.copyFile(lfile, destFile);
+            ensureByMutationFileIsRTF(destFile);
+        }
+
+        // copy file association icons
+        List<Map<String, ? super Object>> fileAssociations =
+                FILE_ASSOCIATIONS.fetchFrom(p);
+
+        for (Map<String, ? super Object> fa : fileAssociations) {
+            File icon = FA_ICON.fetchFrom(fa); // TODO FA_ICON_ICO
+            if (icon == null) {
+                continue;
+            }
+
+            File faIconFile = new File(appDir, icon.getName());
+
+            if (icon.exists()) {
+                try {
+                    IOUtils.copyFile(icon, faIconFile);
+                } catch (IOException e) {
+                    Log.verbose(e);
+                }
+            }
+        }
+
+        return true;
+    }
+
+    public File bundle(Map<String, ? super Object> p, File outdir)
+            throws PackagerException {
+        if (!outdir.isDirectory() && !outdir.mkdirs()) {
+            throw new PackagerException("error.cannot-create-output-dir",
+                    outdir.getAbsolutePath());
+        }
+        if (!outdir.canWrite()) {
+            throw new PackagerException("error.cannot-write-to-output-dir",
+                    outdir.getAbsolutePath());
+        }
+
+        String tempDirectory = WindowsDefender.getUserTempDirectory();
+        if (Arguments.CLIOptions.context().userProvidedBuildRoot) {
+            tempDirectory = BUILD_ROOT.fetchFrom(p).getAbsolutePath();
+        }
+        if (WindowsDefender.isThereAPotentialWindowsDefenderIssue(
+                tempDirectory)) {
+            Log.error(MessageFormat.format(
+                    getString("message.potential.windows.defender.issue"),
+                    tempDirectory));
+        }
+
+        // validate we have valid tools before continuing
+        String iscc = TOOL_INNO_SETUP_COMPILER_EXECUTABLE.fetchFrom(p);
+        if (iscc == null || !new File(iscc).isFile()) {
+            Log.verbose(getString("error.iscc-not-found"));
+            Log.verbose(MessageFormat.format(
+                    getString("message.iscc-file-string"), iscc));
+            throw new PackagerException("error.iscc-not-found");
+        }
+
+        File imageDir = EXE_IMAGE_DIR.fetchFrom(p);
+        try {
+            imageDir.mkdirs();
+
+            boolean menuShortcut = MENU_HINT.fetchFrom(p);
+            boolean desktopShortcut = SHORTCUT_HINT.fetchFrom(p);
+            if (!menuShortcut && !desktopShortcut) {
+                // both can not be false - user will not find the app
+                Log.verbose(getString("message.one-shortcut-required"));
+                p.put(MENU_HINT.getID(), true);
+            }
+
+            if (prepareProto(p) && prepareProjectConfig(p)) {
+                File configScript = getConfig_Script(p);
+                if (configScript.exists()) {
+                    Log.verbose(MessageFormat.format(
+                            getString("message.running-wsh-script"),
+                            configScript.getAbsolutePath()));
+                    IOUtils.run("wscript", configScript, VERBOSE.fetchFrom(p));
+                }
+                return buildEXE(p, outdir);
+            }
+            return null;
+        } catch (IOException ex) {
+            Log.verbose(ex);
+            throw new PackagerException(ex);
+        }
+    }
+
+    // name of post-image script
+    private File getConfig_Script(Map<String, ? super Object> p) {
+        return new File(EXE_IMAGE_DIR.fetchFrom(p),
+                APP_NAME.fetchFrom(p) + "-post-image.wsf");
+    }
+
+    private String getAppIdentifier(Map<String, ? super Object> p) {
+        String nm = UPGRADE_UUID.fetchFrom(p).toString();
+
+        // limitation of innosetup
+        if (nm.length() > 126) {
+            Log.error(getString("message-truncating-id"));
+            nm = nm.substring(0, 126);
+        }
+
+        return nm;
+    }
+
+    private String getLicenseFile(Map<String, ? super Object> p) {
+        String licenseFile = LICENSE_FILE.fetchFrom(p);
+        if (licenseFile != null) {
+            File lfile = new File(licenseFile);
+            File destFile = new File(CONFIG_ROOT.fetchFrom(p), lfile.getName());
+            String filePath = destFile.getAbsolutePath();
+            if (filePath.contains(" ")) {
+                return "\"" + filePath + "\"";
+            } else {
+                return filePath;
+            }
+        }
+
+        return null;
+    }
+
+    void validateValueAndPut(Map<String, String> data, String key,
+                BundlerParamInfo<String> param,
+                Map<String, ? super Object> p) throws IOException {
+        String value = param.fetchFrom(p);
+        if (value.contains("\r") || value.contains("\n")) {
+            throw new IOException("Configuration Parameter " +
+                     param.getID() + " cannot contain multiple lines of text");
+        }
+        data.put(key, innosetupEscape(value));
+    }
+
+    private String innosetupEscape(String value) {
+        if (value == null) {
+            return "";
+        }
+
+        if (value.contains("\"") || !value.trim().equals(value)) {
+            value = "\"" + value.replace("\"", "\"\"") + "\"";
+        }
+        return value;
+    }
+
+    boolean prepareMainProjectFile(Map<String, ? super Object> p)
+            throws IOException {
+        Map<String, String> data = new HashMap<>();
+        data.put("PRODUCT_APP_IDENTIFIER",
+                innosetupEscape(getAppIdentifier(p)));
+
+
+        validateValueAndPut(data, "INSTALLER_NAME", APP_NAME, p);
+        validateValueAndPut(data, "APPLICATION_VENDOR", VENDOR, p);
+        validateValueAndPut(data, "APPLICATION_VERSION", VERSION, p);
+        validateValueAndPut(data, "INSTALLER_FILE_NAME",
+                INSTALLER_FILE_NAME, p);
+
+        data.put("LAUNCHER_NAME",
+                innosetupEscape(WinAppBundler.getAppName(p)));
+
+        data.put("APPLICATION_LAUNCHER_FILENAME",
+                innosetupEscape(WinAppBundler.getLauncherName(p)));
+
+        data.put("APPLICATION_DESKTOP_SHORTCUT",
+                SHORTCUT_HINT.fetchFrom(p) ? "returnTrue" : "returnFalse");
+        data.put("APPLICATION_MENU_SHORTCUT",
+                MENU_HINT.fetchFrom(p) ? "returnTrue" : "returnFalse");
+        validateValueAndPut(data, "APPLICATION_GROUP", MENU_GROUP, p);
+        validateValueAndPut(data, "APPLICATION_COMMENTS", TITLE, p);
+        validateValueAndPut(data, "APPLICATION_COPYRIGHT", COPYRIGHT, p);
+
+        data.put("APPLICATION_LICENSE_FILE",
+                innosetupEscape(getLicenseFile(p)));
+        data.put("DISABLE_DIR_PAGE",
+                INSTALLDIR_CHOOSER.fetchFrom(p) ? "No" : "Yes");
+
+        Boolean isSystemWide = EXE_SYSTEM_WIDE.fetchFrom(p);
+
+        if (isSystemWide) {
+            data.put("APPLICATION_INSTALL_ROOT", "{pf}");
+            data.put("APPLICATION_INSTALL_PRIVILEGE", "admin");
+        } else {
+            data.put("APPLICATION_INSTALL_ROOT", "{localappdata}");
+            data.put("APPLICATION_INSTALL_PRIVILEGE", "lowest");
+        }
+
+        data.put("ARCHITECTURE_BIT_MODE", "x64");
+
+        validateValueAndPut(data, "RUN_FILENAME", APP_NAME, p);
+
+        validateValueAndPut(data, "APPLICATION_DESCRIPTION",
+                DESCRIPTION, p);
+
+        data.put("APPLICATION_SERVICE", "returnFalse");
+        data.put("APPLICATION_NOT_SERVICE", "returnFalse");
+        data.put("APPLICATION_APP_CDS_INSTALL", "returnFalse");
+        data.put("START_ON_INSTALL", "");
+        data.put("STOP_ON_UNINSTALL", "");
+        data.put("RUN_AT_STARTUP", "");
+
+        String imagePathString =
+                WIN_APP_IMAGE.fetchFrom(p).toPath().toAbsolutePath().toString();
+        data.put("APPLICATION_IMAGE", innosetupEscape(imagePathString));
+        Log.verbose("setting APPLICATION_IMAGE to " +
+                innosetupEscape(imagePathString) + " for InnoSetup");
+
+        StringBuilder secondaryLaunchersCfg = new StringBuilder();
+        for (Map<String, ? super Object>
+                launcher : SECONDARY_LAUNCHERS.fetchFrom(p)) {
+            String application_name = APP_NAME.fetchFrom(launcher);
+            if (MENU_HINT.fetchFrom(launcher)) {
+                // Name: "{group}\APPLICATION_NAME";
+                // Filename: "{app}\APPLICATION_NAME.exe";
+                // IconFilename: "{app}\APPLICATION_NAME.ico"
+                secondaryLaunchersCfg.append("Name: \"{group}\\");
+                secondaryLaunchersCfg.append(application_name);
+                secondaryLaunchersCfg.append("\"; Filename: \"{app}\\");
+                secondaryLaunchersCfg.append(application_name);
+                secondaryLaunchersCfg.append(".exe\"; IconFilename: \"{app}\\");
+                secondaryLaunchersCfg.append(application_name);
+                secondaryLaunchersCfg.append(".ico\"\r\n");
+            }
+            if (SHORTCUT_HINT.fetchFrom(launcher)) {
+                // Name: "{commondesktop}\APPLICATION_NAME";
+                // Filename: "{app}\APPLICATION_NAME.exe";
+                // IconFilename: "{app}\APPLICATION_NAME.ico"
+                secondaryLaunchersCfg.append("Name: \"{commondesktop}\\");
+                secondaryLaunchersCfg.append(application_name);
+                secondaryLaunchersCfg.append("\"; Filename: \"{app}\\");
+                secondaryLaunchersCfg.append(application_name);
+                secondaryLaunchersCfg.append(".exe\";  IconFilename: \"{app}\\");
+                secondaryLaunchersCfg.append(application_name);
+                secondaryLaunchersCfg.append(".ico\"\r\n");
+            }
+        }
+        data.put("SECONDARY_LAUNCHERS", secondaryLaunchersCfg.toString());
+
+        StringBuilder registryEntries = new StringBuilder();
+        String regName = APP_REGISTRY_NAME.fetchFrom(p);
+        List<Map<String, ? super Object>> fetchFrom =
+                FILE_ASSOCIATIONS.fetchFrom(p);
+        for (int i = 0; i < fetchFrom.size(); i++) {
+            Map<String, ? super Object> fileAssociation = fetchFrom.get(i);
+            String description = FA_DESCRIPTION.fetchFrom(fileAssociation);
+            File icon = FA_ICON.fetchFrom(fileAssociation); //TODO FA_ICON_ICO
+
+            List<String> extensions = FA_EXTENSIONS.fetchFrom(fileAssociation);
+            String entryName = regName + "File";
+            if (i > 0) {
+                entryName += "." + i;
+            }
+
+            if (extensions == null) {
+                Log.verbose(getString(
+                        "message.creating-association-with-null-extension"));
+            } else {
+                for (String ext : extensions) {
+                    if (isSystemWide) {
+                        // "Root: HKCR; Subkey: \".myp\";
+                        // ValueType: string; ValueName: \"\";
+                        // ValueData: \"MyProgramFile\";
+                        // Flags: uninsdeletevalue"
+                        registryEntries.append("Root: HKCR; Subkey: \".")
+                                .append(ext)
+                                .append("\"; ValueType: string;"
+                                + " ValueName: \"\"; ValueData: \"")
+                                .append(entryName)
+                                .append("\"; Flags: uninsdeletevalue uninsdeletekeyifempty\r\n");
+                    } else {
+                        registryEntries.append(
+                                "Root: HKCU; Subkey: \"Software\\Classes\\.")
+                                .append(ext)
+                                .append("\"; ValueType: string;"
+                                + " ValueName: \"\"; ValueData: \"")
+                                .append(entryName)
+                                .append("\"; Flags: uninsdeletevalue uninsdeletekeyifempty\r\n");
+                    }
+                }
+            }
+
+            if (extensions != null && !extensions.isEmpty()) {
+                String ext = extensions.get(0);
+                List<String> mimeTypes =
+                        FA_CONTENT_TYPE.fetchFrom(fileAssociation);
+                for (String mime : mimeTypes) {
+                    if (isSystemWide) {
+                        // "Root: HKCR;
+                        // Subkey: HKCR\\Mime\\Database\\
+                        //         Content Type\\application/chaos;
+                        // ValueType: string;
+                        // ValueName: Extension;
+                        // ValueData: .chaos;
+                        // Flags: uninsdeletevalue"
+                        registryEntries.append("Root: HKCR; Subkey: " +
+                                 "\"Mime\\Database\\Content Type\\")
+                            .append(mime)
+                            .append("\"; ValueType: string; ValueName: " +
+                                 "\"Extension\"; ValueData: \".")
+                            .append(ext)
+                            .append("\"; Flags: uninsdeletevalue uninsdeletekeyifempty\r\n");
+                    } else {
+                        registryEntries.append(
+                                "Root: HKCU; Subkey: \"Software\\" +
+                                "Classes\\Mime\\Database\\Content Type\\")
+                                .append(mime)
+                                .append("\"; ValueType: string; " +
+                                "ValueName: \"Extension\"; ValueData: \".")
+                                .append(ext)
+                                .append("\"; Flags: uninsdeletevalue uninsdeletekeyifempty\r\n");
+                    }
+                }
+            }
+
+            if (isSystemWide) {
+                // "Root: HKCR;
+                // Subkey: \"MyProgramFile\";
+                // ValueType: string;
+                // ValueName: \"\";
+                // ValueData: \"My Program File\";
+                // Flags: uninsdeletekey"
+                registryEntries.append("Root: HKCR; Subkey: \"")
+                    .append(entryName)
+                    .append(
+                    "\"; ValueType: string; ValueName: \"\"; ValueData: \"")
+                    .append(removeQuotes(description))
+                    .append("\"; Flags: uninsdeletekey\r\n");
+            } else {
+                registryEntries.append(
+                    "Root: HKCU; Subkey: \"Software\\Classes\\")
+                    .append(entryName)
+                    .append(
+                    "\"; ValueType: string; ValueName: \"\"; ValueData: \"")
+                    .append(removeQuotes(description))
+                    .append("\"; Flags: uninsdeletekey\r\n");
+            }
+
+            if (icon != null && icon.exists()) {
+                if (isSystemWide) {
+                    // "Root: HKCR;
+                    // Subkey: \"MyProgramFile\\DefaultIcon\";
+                    // ValueType: string;
+                    // ValueName: \"\";
+                    // ValueData: \"{app}\\MYPROG.EXE,0\"\n" +
+                    registryEntries.append("Root: HKCR; Subkey: \"")
+                            .append(entryName)
+                            .append("\\DefaultIcon\"; ValueType: string; " +
+                            "ValueName: \"\"; ValueData: \"{app}\\")
+                            .append(icon.getName())
+                            .append("\"\r\n");
+                } else {
+                    registryEntries.append(
+                            "Root: HKCU; Subkey: \"Software\\Classes\\")
+                            .append(entryName)
+                            .append("\\DefaultIcon\"; ValueType: string; " +
+                            "ValueName: \"\"; ValueData: \"{app}\\")
+                            .append(icon.getName())
+                            .append("\"\r\n");
+                }
+            }
+
+            if (isSystemWide) {
+                // "Root: HKCR;
+                // Subkey: \"MyProgramFile\\shell\\open\\command\";
+                // ValueType: string;
+                // ValueName: \"\";
+                // ValueData: \"\"\"{app}\\MYPROG.EXE\"\" \"\"%1\"\"\"\n"
+                registryEntries.append("Root: HKCR; Subkey: \"")
+                        .append(entryName)
+                        .append("\\shell\\open\\command\"; ValueType: " +
+                        "string; ValueName: \"\"; ValueData: \"\"\"{app}\\")
+                        .append(APP_NAME.fetchFrom(p))
+                        .append("\"\" \"\"%1\"\"\"\r\n");
+            } else {
+                registryEntries.append(
+                        "Root: HKCU; Subkey: \"Software\\Classes\\")
+                        .append(entryName)
+                        .append("\\shell\\open\\command\"; ValueType: " +
+                        "string; ValueName: \"\"; ValueData: \"\"\"{app}\\")
+                        .append(APP_NAME.fetchFrom(p))
+                        .append("\"\" \"\"%1\"\"\"\r\n");
+            }
+        }
+        if (registryEntries.length() > 0) {
+            data.put("FILE_ASSOCIATIONS",
+                    "ChangesAssociations=yes\r\n\r\n[Registry]\r\n" +
+                    registryEntries.toString());
+        } else {
+            data.put("FILE_ASSOCIATIONS", "");
+        }
+
+        // TODO - alternate template for JRE installer
+        String iss = RUNTIME_INSTALLER.fetchFrom(p) ?
+                DEFAULT_JRE_EXE_TEMPLATE : DEFAULT_EXE_PROJECT_TEMPLATE;
+
+        Writer w = new BufferedWriter(new FileWriter(
+                getConfig_ExeProjectFile(p)));
+
+        String content = preprocessTextResource(
+                getConfig_ExeProjectFile(p).getName(),
+                getString("resource.inno-setup-project-file"),
+                iss, data, VERBOSE.fetchFrom(p),
+                RESOURCE_DIR.fetchFrom(p));
+        w.write(content);
+        w.close();
+        return true;
+    }
+
+    private final static String removeQuotes(String s) {
+        if (s.length() > 2 && s.startsWith("\"") && s.endsWith("\"")) {
+            // special case for '"XXX"' return 'XXX' not '-XXX-'
+            // note '"' and '""' are excluded from this special case
+            s = s.substring(1, s.length() - 1);
+        }
+        // if there interior double quotes replace them with '-'
+        return s.replaceAll("\"", "-");
+    }
+
+    private final static String DEFAULT_INNO_SETUP_ICON =
+            "icon_inno_setup.bmp";
+
+    private boolean prepareProjectConfig(Map<String, ? super Object> p)
+            throws IOException {
+        prepareMainProjectFile(p);
+
+        // prepare installer icon
+        File iconTarget = getConfig_SmallInnoSetupIcon(p);
+        fetchResource(iconTarget.getName(),
+                getString("resource.setup-icon"),
+                DEFAULT_INNO_SETUP_ICON,
+                iconTarget,
+                VERBOSE.fetchFrom(p),
+                RESOURCE_DIR.fetchFrom(p));
+
+        fetchResource(getConfig_Script(p).getName(),
+                getString("resource.post-install-script"),
+                (String) null,
+                getConfig_Script(p),
+                VERBOSE.fetchFrom(p),
+                RESOURCE_DIR.fetchFrom(p));
+        return true;
+    }
+
+    private File getConfig_SmallInnoSetupIcon(
+            Map<String, ? super Object> p) {
+        return new File(EXE_IMAGE_DIR.fetchFrom(p),
+                APP_NAME.fetchFrom(p) + "-setup-icon.bmp");
+    }
+
+    private File getConfig_ExeProjectFile(Map<String, ? super Object> p) {
+        return new File(EXE_IMAGE_DIR.fetchFrom(p),
+                APP_NAME.fetchFrom(p) + ".iss");
+    }
+
+
+    private File buildEXE(Map<String, ? super Object> p, File outdir)
+             throws IOException {
+        Log.verbose(MessageFormat.format(
+             getString("message.outputting-to-location"),
+             outdir.getAbsolutePath()));
+
+        outdir.mkdirs();
+
+        // run Inno Setup
+        ProcessBuilder pb = new ProcessBuilder(
+                TOOL_INNO_SETUP_COMPILER_EXECUTABLE.fetchFrom(p),
+                "/q",    // turn off inno setup output
+                "/o"+outdir.getAbsolutePath(),
+                getConfig_ExeProjectFile(p).getAbsolutePath());
+        pb = pb.directory(EXE_IMAGE_DIR.fetchFrom(p));
+        IOUtils.exec(pb, VERBOSE.fetchFrom(p));
+
+        Log.verbose(MessageFormat.format(
+                getString("message.output-location"),
+                outdir.getAbsolutePath()));
+
+        // presume the result is the ".exe" file with the newest modified time
+        // not the best solution, but it is the most reliable
+        File result = null;
+        long lastModified = 0;
+        File[] list = outdir.listFiles();
+        if (list != null) {
+            for (File f : list) {
+                if (f.getName().endsWith(".exe") &&
+                        f.lastModified() > lastModified) {
+                    result = f;
+                    lastModified = f.lastModified();
+                }
+            }
+        }
+
+        return result;
+    }
+
+   public static void ensureByMutationFileIsRTF(File f) {
+        if (f == null || !f.isFile()) return;
+
+        try {
+            boolean existingLicenseIsRTF = false;
+
+            try (FileInputStream fin = new FileInputStream(f)) {
+                byte[] firstBits = new byte[7];
+
+                if (fin.read(firstBits) == firstBits.length) {
+                    String header = new String(firstBits);
+                    existingLicenseIsRTF = "{\\rtf1\\".equals(header);
+                }
+            }
+
+            if (!existingLicenseIsRTF) {
+                List<String> oldLicense = Files.readAllLines(f.toPath());
+                try (Writer w = Files.newBufferedWriter(
+                        f.toPath(), Charset.forName("Windows-1252"))) {
+                    w.write("{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang1033"
+                            + "{\\fonttbl{\\f0\\fnil\\fcharset0 Arial;}}\n"
+                            + "\\viewkind4\\uc1\\pard\\sa200\\sl276"
+                            + "\\slmult1\\lang9\\fs20 ");
+                    oldLicense.forEach(l -> {
+                        try {
+                            for (char c : l.toCharArray()) {
+                                if (c < 0x10) {
+                                    w.write("\\'0");
+                                    w.write(Integer.toHexString(c));
+                                } else if (c > 0xff) {
+                                    w.write("\\ud");
+                                    w.write(Integer.toString(c));
+                                    w.write("?");
+                                } else if ((c < 0x20) || (c >= 0x80) ||
+                                        (c == 0x5C) || (c == 0x7B) ||
+                                        (c == 0x7D)) {
+                                    w.write("\\'");
+                                    w.write(Integer.toHexString(c));
+                                } else {
+                                    w.write(c);
+                                }
+                            }
+                            if (l.length() < 1) {
+                                w.write("\\par");
+                            } else {
+                                w.write(" ");
+                            }
+                            w.write("\r\n");
+                        } catch (IOException e) {
+                            Log.verbose(e);
+                        }
+                    });
+                    w.write("}\r\n");
+                }
+            }
+        } catch (IOException e) {
+            Log.verbose(e);
+        }
+    }
+
+    private static String getString(String key)
+            throws MissingResourceException {
+        return I18N.getString(key);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinMsiBundler.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,1164 @@
+/*
+ * Copyright (c) 2012, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jpackage.internal;
+
+import java.io.*;
+import java.nio.charset.Charset;
+import java.nio.file.Files;
+import java.text.MessageFormat;
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import static jdk.jpackage.internal.WindowsBundlerParam.*;
+
+public class WinMsiBundler  extends AbstractBundler {
+
+    private static final ResourceBundle I18N = ResourceBundle.getBundle(
+            "jdk.jpackage.internal.resources.WinResources");
+
+    public static final BundlerParamInfo<WinAppBundler> APP_BUNDLER =
+            new WindowsBundlerParam<>(
+            I18N.getString("param.msi-bundler.name"),
+            I18N.getString("param.msi-bundler.description"),
+            "win.app.bundler",
+            WinAppBundler.class,
+            params -> new WinAppBundler(),
+            null);
+
+    public static final BundlerParamInfo<Boolean> CAN_USE_WIX36 =
+            new WindowsBundlerParam<>(
+            I18N.getString("param.can-use-wix36.name"),
+            I18N.getString("param.can-use-wix36.description"),
+            "win.msi.canUseWix36",
+            Boolean.class,
+            params -> false,
+            (s, p) -> Boolean.valueOf(s));
+
+    public static final BundlerParamInfo<File> MSI_IMAGE_DIR =
+            new WindowsBundlerParam<>(
+            I18N.getString("param.image-dir.name"),
+            I18N.getString("param.image-dir.description"),
+            "win.msi.imageDir",
+            File.class,
+            params -> {
+                File imagesRoot = IMAGES_ROOT.fetchFrom(params);
+                if (!imagesRoot.exists()) imagesRoot.mkdirs();
+                return new File(imagesRoot, "win-msi.image");
+            },
+            (s, p) -> null);
+
+    public static final BundlerParamInfo<File> WIN_APP_IMAGE =
+            new WindowsBundlerParam<>(
+            I18N.getString("param.app-dir.name"),
+            I18N.getString("param.app-dir.description"),
+            "win.app.image",
+            File.class,
+            null,
+            (s, p) -> null);
+
+    public static final StandardBundlerParam<Boolean> MSI_SYSTEM_WIDE  =
+            new StandardBundlerParam<>(
+                    I18N.getString("param.system-wide.name"),
+                    I18N.getString("param.system-wide.description"),
+                    Arguments.CLIOptions.WIN_PER_USER_INSTALLATION.getId(),
+                    Boolean.class,
+                    params -> true, // MSIs default to system wide
+                    // valueOf(null) is false,
+                    // and we actually do want null
+                    (s, p) -> (s == null || "null".equalsIgnoreCase(s))? null
+                            : Boolean.valueOf(s)
+            );
+
+
+    public static final StandardBundlerParam<String> PRODUCT_VERSION =
+            new StandardBundlerParam<>(
+                    I18N.getString("param.product-version.name"),
+                    I18N.getString("param.product-version.description"),
+                    "win.msi.productVersion",
+                    String.class,
+                    VERSION::fetchFrom,
+                    (s, p) -> s
+            );
+
+    public static final BundlerParamInfo<UUID> UPGRADE_UUID =
+            new WindowsBundlerParam<>(
+            I18N.getString("param.upgrade-uuid.name"),
+            I18N.getString("param.upgrade-uuid.description"),
+            Arguments.CLIOptions.WIN_UPGRADE_UUID.getId(),
+            UUID.class,
+            params -> UUID.randomUUID(),
+            (s, p) -> UUID.fromString(s));
+
+    private static final String TOOL_CANDLE = "candle.exe";
+    private static final String TOOL_LIGHT = "light.exe";
+    // autodetect just v3.7, v3.8, 3.9, 3.10 and 3.11
+    private static final String AUTODETECT_DIRS =
+            ";C:\\Program Files (x86)\\WiX Toolset v3.11\\bin;"
+            + "C:\\Program Files\\WiX Toolset v3.11\\bin;"
+            + "C:\\Program Files (x86)\\WiX Toolset v3.10\\bin;"
+            + "C:\\Program Files\\WiX Toolset v3.10\\bin;"
+            + "C:\\Program Files (x86)\\WiX Toolset v3.9\\bin;"
+            + "C:\\Program Files\\WiX Toolset v3.9\\bin;"
+            + "C:\\Program Files (x86)\\WiX Toolset v3.8\\bin;"
+            + "C:\\Program Files\\WiX Toolset v3.8\\bin;"
+            + "C:\\Program Files (x86)\\WiX Toolset v3.7\\bin;"
+            + "C:\\Program Files\\WiX Toolset v3.7\\bin";
+
+    public static final BundlerParamInfo<String> TOOL_CANDLE_EXECUTABLE =
+            new WindowsBundlerParam<>(
+            I18N.getString("param.candle-path.name"),
+            I18N.getString("param.candle-path.description"),
+            "win.msi.candle.exe",
+            String.class,
+            params -> {
+                for (String dirString : (System.getenv("PATH") +
+                        AUTODETECT_DIRS).split(";")) {
+                    File f = new File(dirString.replace("\"", ""), TOOL_CANDLE);
+                    if (f.isFile()) {
+                        return f.toString();
+                    }
+                }
+                return null;
+            },
+            null);
+
+    public static final BundlerParamInfo<String> TOOL_LIGHT_EXECUTABLE =
+            new WindowsBundlerParam<>(
+            I18N.getString("param.light-path.name"),
+            I18N.getString("param.light-path.description"),
+            "win.msi.light.exe",
+            String.class,
+            params -> {
+                for (String dirString : (System.getenv("PATH") +
+                        AUTODETECT_DIRS).split(";")) {
+                    File f = new File(dirString.replace("\"", ""), TOOL_LIGHT);
+                    if (f.isFile()) {
+                        return f.toString();
+                    }
+                }
+                return null;
+            },
+            null);
+
+    public static final StandardBundlerParam<Boolean> MENU_HINT =
+        new WindowsBundlerParam<>(
+                I18N.getString("param.menu-shortcut-hint.name"),
+                I18N.getString("param.menu-shortcut-hint.description"),
+                Arguments.CLIOptions.WIN_MENU_HINT.getId(),
+                Boolean.class,
+                params -> false,
+                // valueOf(null) is false,
+                // and we actually do want null in some cases
+                (s, p) -> (s == null ||
+                        "null".equalsIgnoreCase(s))? true : Boolean.valueOf(s)
+        );
+
+    public static final StandardBundlerParam<Boolean> SHORTCUT_HINT =
+        new WindowsBundlerParam<>(
+                I18N.getString("param.desktop-shortcut-hint.name"),
+                I18N.getString("param.desktop-shortcut-hint.description"),
+                Arguments.CLIOptions.WIN_SHORTCUT_HINT.getId(),
+                Boolean.class,
+                params -> false,
+                // valueOf(null) is false,
+                // and we actually do want null in some cases
+                (s, p) -> (s == null ||
+                       "null".equalsIgnoreCase(s))? false : Boolean.valueOf(s)
+        );
+
+    @Override
+    public String getName() {
+        return I18N.getString("msi.bundler.name");
+    }
+
+    @Override
+    public String getDescription() {
+        return I18N.getString("msi.bundler.description");
+    }
+
+    @Override
+    public String getID() {
+        return "msi";
+    }
+
+    @Override
+    public String getBundleType() {
+        return "INSTALLER";
+    }
+
+    @Override
+    public Collection<BundlerParamInfo<?>> getBundleParameters() {
+        Collection<BundlerParamInfo<?>> results = new LinkedHashSet<>();
+        results.addAll(WinAppBundler.getAppBundleParameters());
+        results.addAll(getMsiBundleParameters());
+        return results;
+    }
+
+    public static Collection<BundlerParamInfo<?>> getMsiBundleParameters() {
+        return Arrays.asList(
+                DESCRIPTION,
+                MENU_GROUP,
+                MENU_HINT,
+                PRODUCT_VERSION,
+                SHORTCUT_HINT,
+                MSI_SYSTEM_WIDE,
+                VENDOR,
+                LICENSE_FILE,
+                INSTALLDIR_CHOOSER
+        );
+    }
+
+    @Override
+    public File execute(Map<String, ? super Object> params,
+            File outputParentDir) throws PackagerException {
+        return bundle(params, outputParentDir);
+    }
+
+    @Override
+    public boolean supported(boolean platformInstaller) {
+        return (Platform.getPlatform() == Platform.WINDOWS);
+    }
+
+    private static String findToolVersion(String toolName) {
+        try {
+            if (toolName == null || "".equals(toolName)) return null;
+
+            ProcessBuilder pb = new ProcessBuilder(
+                    toolName,
+                    "/?");
+            VersionExtractor ve = new VersionExtractor("version (\\d+.\\d+)");
+            // not interested in the output
+            IOUtils.exec(pb, Log.isDebug(), true, ve);
+            String version = ve.getVersion();
+            Log.verbose(MessageFormat.format(
+                    I18N.getString("message.tool-version"),
+                    toolName, version));
+            return version;
+        } catch (Exception e) {
+            if (Log.isDebug()) {
+                Log.verbose(e);
+            }
+            return null;
+        }
+    }
+
+    @Override
+    public boolean validate(Map<String, ? super Object> p)
+            throws UnsupportedPlatformException, ConfigException {
+        try {
+            if (p == null) throw new ConfigException(
+                    I18N.getString("error.parameters-null"),
+                    I18N.getString("error.parameters-null.advice"));
+
+            // run basic validation to ensure requirements are met
+            // we are not interested in return code, only possible exception
+            APP_BUNDLER.fetchFrom(p).validate(p);
+
+            String candleVersion =
+                    findToolVersion(TOOL_CANDLE_EXECUTABLE.fetchFrom(p));
+            String lightVersion =
+                    findToolVersion(TOOL_LIGHT_EXECUTABLE.fetchFrom(p));
+
+            // WiX 3.0+ is required
+            String minVersion = "3.0";
+            boolean bad = false;
+
+            if (VersionExtractor.isLessThan(candleVersion, minVersion)) {
+                Log.verbose(MessageFormat.format(
+                        I18N.getString("message.wrong-tool-version"),
+                        TOOL_CANDLE, candleVersion, minVersion));
+                bad = true;
+            }
+            if (VersionExtractor.isLessThan(lightVersion, minVersion)) {
+                Log.verbose(MessageFormat.format(
+                        I18N.getString("message.wrong-tool-version"),
+                        TOOL_LIGHT, lightVersion, minVersion));
+                bad = true;
+            }
+
+            if (bad){
+                throw new ConfigException(
+                        I18N.getString("error.no-wix-tools"),
+                        I18N.getString("error.no-wix-tools.advice"));
+            }
+
+            if (!VersionExtractor.isLessThan(lightVersion, "3.6")) {
+                Log.verbose(I18N.getString("message.use-wix36-features"));
+                p.put(CAN_USE_WIX36.getID(), Boolean.TRUE);
+            }
+
+            /********* validate bundle parameters *************/
+
+            String version = PRODUCT_VERSION.fetchFrom(p);
+            if (!isVersionStringValid(version)) {
+                throw new ConfigException(
+                        MessageFormat.format(I18N.getString(
+                                "error.version-string-wrong-format"), version),
+                        MessageFormat.format(I18N.getString(
+                                "error.version-string-wrong-format.advice"),
+                                PRODUCT_VERSION.getID()));
+            }
+
+            // only one mime type per association, at least one file extension
+            List<Map<String, ? super Object>> associations =
+                    FILE_ASSOCIATIONS.fetchFrom(p);
+            if (associations != null) {
+                for (int i = 0; i < associations.size(); i++) {
+                    Map<String, ? super Object> assoc = associations.get(i);
+                    List<String> mimes = FA_CONTENT_TYPE.fetchFrom(assoc);
+                    if (mimes.size() > 1) {
+                        throw new ConfigException(MessageFormat.format(
+                                I18N.getString("error.too-many-content-"
+                                + "types-for-file-association"), i),
+                                I18N.getString("error.too-many-content-"
+                                + "types-for-file-association.advice"));
+                    }
+                }
+            }
+
+            return true;
+        } catch (RuntimeException re) {
+            if (re.getCause() instanceof ConfigException) {
+                throw (ConfigException) re.getCause();
+            } else {
+                throw new ConfigException(re);
+            }
+        }
+    }
+
+    // http://msdn.microsoft.com/en-us/library/aa370859%28v=VS.85%29.aspx
+    // The format of the string is as follows:
+    //     major.minor.build
+    // The first field is the major version and has a maximum value of 255.
+    // The second field is the minor version and has a maximum value of 255.
+    // The third field is called the build version or the update version and
+    // has a maximum value of 65,535.
+    static boolean isVersionStringValid(String v) {
+        if (v == null) {
+            return true;
+        }
+
+        String p[] = v.split("\\.");
+        if (p.length > 3) {
+            Log.verbose(I18N.getString(
+                    "message.version-string-too-many-components"));
+            return false;
+        }
+
+        try {
+            int val = Integer.parseInt(p[0]);
+            if (val < 0 || val > 255) {
+                Log.verbose(I18N.getString(
+                        "error.version-string-major-out-of-range"));
+                return false;
+            }
+            if (p.length > 1) {
+                val = Integer.parseInt(p[1]);
+                if (val < 0 || val > 255) {
+                    Log.verbose(I18N.getString(
+                            "error.version-string-minor-out-of-range"));
+                    return false;
+                }
+            }
+            if (p.length > 2) {
+                val = Integer.parseInt(p[2]);
+                if (val < 0 || val > 65535) {
+                    Log.verbose(I18N.getString(
+                            "error.version-string-build-out-of-range"));
+                    return false;
+                }
+            }
+        } catch (NumberFormatException ne) {
+            Log.verbose(I18N.getString("error.version-string-part-not-number"));
+            Log.verbose(ne);
+            return false;
+        }
+
+        return true;
+    }
+
+    private boolean prepareProto(Map<String, ? super Object> p)
+                throws PackagerException, IOException {
+        File appImage = StandardBundlerParam.getPredefinedAppImage(p);
+        File appDir = null;
+
+        // we either have an application image or need to build one
+        if (appImage != null) {
+            appDir = new File(
+                    MSI_IMAGE_DIR.fetchFrom(p), APP_NAME.fetchFrom(p));
+            // copy everything from appImage dir into appDir/name
+            IOUtils.copyRecursive(appImage.toPath(), appDir.toPath());
+        } else {
+            appDir = APP_BUNDLER.fetchFrom(p).doBundle(p,
+                    MSI_IMAGE_DIR.fetchFrom(p), true);
+        }
+
+        p.put(WIN_APP_IMAGE.getID(), appDir);
+
+        String licenseFile = LICENSE_FILE.fetchFrom(p);
+        if (licenseFile != null) {
+            // need to copy license file to the working directory and convert to rtf if needed
+            File lfile = new File(licenseFile);
+            File destFile = new File(CONFIG_ROOT.fetchFrom(p), lfile.getName());
+            IOUtils.copyFile(lfile, destFile);
+            ensureByMutationFileIsRTF(destFile);
+        }
+
+        // copy file association icons
+        List<Map<String, ? super Object>> fileAssociations =
+                FILE_ASSOCIATIONS.fetchFrom(p);
+        for (Map<String, ? super Object> fa : fileAssociations) {
+            File icon = FA_ICON.fetchFrom(fa); // TODO FA_ICON_ICO
+            if (icon == null) {
+                continue;
+            }
+
+            File faIconFile = new File(appDir, icon.getName());
+
+            if (icon.exists()) {
+                try {
+                    IOUtils.copyFile(icon, faIconFile);
+                } catch (IOException e) {
+                    Log.verbose(e);
+                }
+            }
+        }
+
+        return appDir != null;
+    }
+
+    public File bundle(Map<String, ? super Object> p, File outdir)
+            throws PackagerException {
+        if (!outdir.isDirectory() && !outdir.mkdirs()) {
+            throw new PackagerException("error.cannot-create-output-dir",
+                    outdir.getAbsolutePath());
+        }
+        if (!outdir.canWrite()) {
+            throw new PackagerException("error.cannot-write-to-output-dir",
+                    outdir.getAbsolutePath());
+        }
+
+        // validate we have valid tools before continuing
+        String light = TOOL_LIGHT_EXECUTABLE.fetchFrom(p);
+        String candle = TOOL_CANDLE_EXECUTABLE.fetchFrom(p);
+        if (light == null || !new File(light).isFile() ||
+            candle == null || !new File(candle).isFile()) {
+            Log.verbose(MessageFormat.format(
+                   I18N.getString("message.light-file-string"), light));
+            Log.verbose(MessageFormat.format(
+                   I18N.getString("message.candle-file-string"), candle));
+            throw new PackagerException("error.no-wix-tools");
+        }
+
+        File imageDir = MSI_IMAGE_DIR.fetchFrom(p);
+        try {
+            imageDir.mkdirs();
+
+            boolean menuShortcut = MENU_HINT.fetchFrom(p);
+            boolean desktopShortcut = SHORTCUT_HINT.fetchFrom(p);
+            if (!menuShortcut && !desktopShortcut) {
+                // both can not be false - user will not find the app
+                Log.verbose(I18N.getString("message.one-shortcut-required"));
+                p.put(MENU_HINT.getID(), true);
+            }
+
+            if (prepareProto(p) && prepareWiXConfig(p)
+                    && prepareBasicProjectConfig(p)) {
+                File configScriptSrc = getConfig_Script(p);
+                if (configScriptSrc.exists()) {
+                    // we need to be running post script in the image folder
+
+                    // NOTE: Would it be better to generate it to the image
+                    // folder and save only if "verbose" is requested?
+
+                    // for now we replicate it
+                    File configScript =
+                        new File(imageDir, configScriptSrc.getName());
+                    IOUtils.copyFile(configScriptSrc, configScript);
+                    Log.verbose(MessageFormat.format(
+                            I18N.getString("message.running-wsh-script"),
+                            configScript.getAbsolutePath()));
+                    IOUtils.run("wscript",
+                             configScript, false);
+                }
+                return buildMSI(p, outdir);
+            }
+            return null;
+        } catch (IOException ex) {
+            Log.verbose(ex);
+            throw new PackagerException(ex);
+        }
+    }
+
+    // name of post-image script
+    private File getConfig_Script(Map<String, ? super Object> params) {
+        return new File(CONFIG_ROOT.fetchFrom(params),
+                APP_NAME.fetchFrom(params) + "-post-image.wsf");
+    }
+
+    private boolean prepareBasicProjectConfig(
+        Map<String, ? super Object> params) throws IOException {
+        fetchResource(getConfig_Script(params).getName(),
+                I18N.getString("resource.post-install-script"),
+                (String) null,
+                getConfig_Script(params),
+                VERBOSE.fetchFrom(params),
+                RESOURCE_DIR.fetchFrom(params));
+        return true;
+    }
+
+    private String relativePath(File basedir, File file) {
+        return file.getAbsolutePath().substring(
+                basedir.getAbsolutePath().length() + 1);
+    }
+
+    boolean prepareMainProjectFile(
+            Map<String, ? super Object> params) throws IOException {
+        Map<String, String> data = new HashMap<>();
+
+        UUID productGUID = UUID.randomUUID();
+
+        Log.verbose(MessageFormat.format(
+                I18N.getString("message.generated-product-guid"),
+                productGUID.toString()));
+
+        // we use random GUID for product itself but
+        // user provided for upgrade guid
+        // Upgrade guid is important to decide whether it is an upgrade of
+        // installed app.  I.e. we need it to be the same for
+        // 2 different versions of app if possible
+        data.put("PRODUCT_GUID", productGUID.toString());
+        data.put("PRODUCT_UPGRADE_GUID",
+                UPGRADE_UUID.fetchFrom(params).toString());
+        data.put("UPGRADE_BLOCK", getUpgradeBlock(params));
+
+        data.put("APPLICATION_NAME", APP_NAME.fetchFrom(params));
+        data.put("APPLICATION_DESCRIPTION", DESCRIPTION.fetchFrom(params));
+        data.put("APPLICATION_VENDOR", VENDOR.fetchFrom(params));
+        data.put("APPLICATION_VERSION", PRODUCT_VERSION.fetchFrom(params));
+
+        // WinAppBundler will add application folder again => step out
+        File imageRootDir = WIN_APP_IMAGE.fetchFrom(params);
+        File launcher = new File(imageRootDir,
+                WinAppBundler.getLauncherName(params));
+
+        String launcherPath = relativePath(imageRootDir, launcher);
+        data.put("APPLICATION_LAUNCHER", launcherPath);
+
+        String iconPath = launcherPath.replace(".exe", ".ico");
+
+        data.put("APPLICATION_ICON", iconPath);
+
+        data.put("REGISTRY_ROOT", getRegistryRoot(params));
+
+        boolean canUseWix36Features = CAN_USE_WIX36.fetchFrom(params);
+        data.put("WIX36_ONLY_START",
+                canUseWix36Features ? "" : "<!--");
+        data.put("WIX36_ONLY_END",
+                canUseWix36Features ? "" : "-->");
+
+        if (MSI_SYSTEM_WIDE.fetchFrom(params)) {
+            data.put("INSTALL_SCOPE", "perMachine");
+        } else {
+            data.put("INSTALL_SCOPE", "perUser");
+        }
+
+        data.put("PLATFORM", "x64");
+        data.put("WIN64", "yes");
+
+        data.put("UI_BLOCK", getUIBlock(params));
+
+        List<Map<String, ? super Object>> secondaryLaunchers =
+                SECONDARY_LAUNCHERS.fetchFrom(params);
+
+        StringBuilder secondaryLauncherIcons = new StringBuilder();
+        for (int i = 0; i < secondaryLaunchers.size(); i++) {
+            Map<String, ? super Object> sl = secondaryLaunchers.get(i);
+            // <Icon Id="DesktopIcon.exe" SourceFile="APPLICATION_ICON" />
+            if (SHORTCUT_HINT.fetchFrom(sl) || MENU_HINT.fetchFrom(sl)) {
+                File secondaryLauncher = new File(imageRootDir,
+                        WinAppBundler.getLauncherName(sl));
+                String secondaryLauncherPath =
+                        relativePath(imageRootDir, secondaryLauncher);
+                String secondaryLauncherIconPath =
+                        secondaryLauncherPath.replace(".exe", ".ico");
+
+                secondaryLauncherIcons.append("        <Icon Id=\"Launcher");
+                secondaryLauncherIcons.append(i);
+                secondaryLauncherIcons.append(".exe\" SourceFile=\"");
+                secondaryLauncherIcons.append(secondaryLauncherIconPath);
+                secondaryLauncherIcons.append("\" />\r\n");
+            }
+        }
+        data.put("SECONDARY_LAUNCHER_ICONS", secondaryLauncherIcons.toString());
+
+        String wxs = RUNTIME_INSTALLER.fetchFrom(params) ?
+                MSI_PROJECT_TEMPLATE_SERVER_JRE : MSI_PROJECT_TEMPLATE;
+
+        Writer w = new BufferedWriter(
+                new FileWriter(getConfig_ProjectFile(params)));
+
+        String content = preprocessTextResource(
+                getConfig_ProjectFile(params).getName(),
+                I18N.getString("resource.wix-config-file"),
+                wxs, data, VERBOSE.fetchFrom(params),
+                RESOURCE_DIR.fetchFrom(params));
+        w.write(content);
+        w.close();
+        return true;
+    }
+    private int id;
+    private int compId;
+    private final static String LAUNCHER_ID = "LauncherId";
+
+    /**
+     * Overrides the dialog sequence in built-in dialog set "WixUI_InstallDir"
+     * to exclude license dialog
+     */
+    private static final String TWEAK_FOR_EXCLUDING_LICENSE =
+              "     <Publish Dialog=\"WelcomeDlg\" Control=\"Next\""
+            + "              Event=\"NewDialog\" Value=\"InstallDirDlg\""
+            + " Order=\"2\"> 1"
+            + "     </Publish>\n"
+            + "     <Publish Dialog=\"InstallDirDlg\" Control=\"Back\""
+            + "              Event=\"NewDialog\" Value=\"WelcomeDlg\""
+            + " Order=\"2\"> 1"
+            + "     </Publish>\n";
+
+    // Required upgrade element for installers which support major upgrade (when user
+    // specifies --win-upgrade-uuid). We will allow downgrades.
+    private static final String UPGRADE_BLOCK =
+            "<MajorUpgrade AllowDowngrades=\"yes\"/>";
+
+    private String getUpgradeBlock(Map<String, ? super Object> params) {
+        if (UPGRADE_UUID.getIsDefaultValue()) {
+            return "";
+        } else {
+            return UPGRADE_BLOCK;
+        }
+    }
+
+    /**
+     * Creates UI element using WiX built-in dialog sets
+     *     - WixUI_InstallDir/WixUI_Minimal.
+     * The dialog sets are the closest to what we want to implement.
+     *
+     * WixUI_Minimal for license dialog only
+     * WixUI_InstallDir for installdir dialog only or for both
+     * installdir/license dialogs
+     */
+    private String getUIBlock(Map<String, ? super Object> params) {
+        String uiBlock = "     <UI/>\n"; // UI-less element
+
+        if (INSTALLDIR_CHOOSER.fetchFrom(params)) {
+            boolean enableTweakForExcludingLicense =
+                    (getLicenseFile(params) == null);
+            uiBlock = "     <UI>\n"
+                    + "     <Property Id=\"WIXUI_INSTALLDIR\""
+                    + " Value=\"APPLICATIONFOLDER\" />\n"
+                    + "     <UIRef Id=\"WixUI_InstallDir\" />\n"
+                    + (enableTweakForExcludingLicense ?
+                            TWEAK_FOR_EXCLUDING_LICENSE : "")
+                    +"     </UI>\n";
+        } else if (getLicenseFile(params) != null) {
+            uiBlock = "     <UI>\n"
+                    + "     <UIRef Id=\"WixUI_Minimal\" />\n"
+                    + "     </UI>\n";
+        }
+
+        return uiBlock;
+    }
+
+    private void walkFileTree(Map<String, ? super Object> params,
+            File root, PrintStream out, String prefix) {
+        List<File> dirs = new ArrayList<>();
+        List<File> files = new ArrayList<>();
+
+        if (!root.isDirectory()) {
+            throw new RuntimeException(
+                    MessageFormat.format(
+                            I18N.getString("error.cannot-walk-directory"),
+                            root.getAbsolutePath()));
+        }
+
+        // sort to files and dirs
+        File[] children = root.listFiles();
+        if (children != null) {
+            for (File f : children) {
+                if (f.isDirectory()) {
+                    dirs.add(f);
+                } else {
+                    files.add(f);
+                }
+            }
+        }
+
+        // have files => need to output component
+        out.println(prefix + " <Component Id=\"comp" + (compId++)
+                + "\" DiskId=\"1\""
+                + " Guid=\"" + UUID.randomUUID().toString() + "\""
+                + " Win64=\"yes\""
+                + ">");
+        out.println(prefix + "  <CreateFolder/>");
+        out.println(prefix + "  <RemoveFolder Id=\"RemoveDir"
+                + (id++) + "\" On=\"uninstall\" />");
+
+        boolean needRegistryKey = !MSI_SYSTEM_WIDE.fetchFrom(params);
+        File imageRootDir = WIN_APP_IMAGE.fetchFrom(params);
+        File launcherFile =
+                new File(imageRootDir, WinAppBundler.getLauncherName(params));
+
+        // Find out if we need to use registry. We need it if
+        //  - we doing user level install as file can not serve as KeyPath
+        //  - if we adding shortcut in this component
+
+        for (File f: files) {
+            boolean isLauncher = f.equals(launcherFile);
+            if (isLauncher) {
+                needRegistryKey = true;
+            }
+        }
+
+        if (needRegistryKey) {
+            // has to be under HKCU to make WiX happy
+            out.println(prefix + "    <RegistryKey Root=\"HKCU\" "
+                    + " Key=\"Software\\" + VENDOR.fetchFrom(params) + "\\"
+                    + APP_NAME.fetchFrom(params) + "\""
+                    + (CAN_USE_WIX36.fetchFrom(params) ?
+                    ">" : " Action=\"createAndRemoveOnUninstall\">"));
+            out.println(prefix
+                    + "     <RegistryValue Name=\"Version\" Value=\""
+                    + VERSION.fetchFrom(params)
+                    + "\" Type=\"string\" KeyPath=\"yes\"/>");
+            out.println(prefix + "   </RegistryKey>");
+        }
+
+        boolean menuShortcut = MENU_HINT.fetchFrom(params);
+        boolean desktopShortcut = SHORTCUT_HINT.fetchFrom(params);
+
+        Map<String, String> idToFileMap = new TreeMap<>();
+        boolean launcherSet = false;
+
+        for (File f : files) {
+            boolean isLauncher = f.equals(launcherFile);
+
+            launcherSet = launcherSet || isLauncher;
+
+            boolean doShortcuts =
+                isLauncher && (menuShortcut || desktopShortcut);
+
+            String thisFileId = isLauncher ? LAUNCHER_ID : ("FileId" + (id++));
+            idToFileMap.put(f.getName(), thisFileId);
+
+            out.println(prefix + "   <File Id=\"" +
+                    thisFileId + "\""
+                    + " Name=\"" + f.getName() + "\" "
+                    + " Source=\"" + relativePath(imageRootDir, f) + "\""
+                    + " ProcessorArchitecture=\"x64\"" + ">");
+            if (doShortcuts && desktopShortcut) {
+                out.println(prefix
+                        + "  <Shortcut Id=\"desktopShortcut\" Directory="
+                        + "\"DesktopFolder\""
+                        + " Name=\"" + APP_NAME.fetchFrom(params)
+                        + "\" WorkingDirectory=\"INSTALLDIR\""
+                        + " Advertise=\"no\" Icon=\"DesktopIcon.exe\""
+                        + " IconIndex=\"0\" />");
+            }
+            if (doShortcuts && menuShortcut) {
+                out.println(prefix
+                        + "     <Shortcut Id=\"ExeShortcut\" Directory="
+                        + "\"ProgramMenuDir\""
+                        + " Name=\"" + APP_NAME.fetchFrom(params)
+                        + "\" Advertise=\"no\" Icon=\"StartMenuIcon.exe\""
+                        + " IconIndex=\"0\" />");
+            }
+
+            List<Map<String, ? super Object>> secondaryLaunchers =
+                    SECONDARY_LAUNCHERS.fetchFrom(params);
+            for (int i = 0; i < secondaryLaunchers.size(); i++) {
+                Map<String, ? super Object> sl = secondaryLaunchers.get(i);
+                File secondaryLauncherFile = new File(imageRootDir,
+                        WinAppBundler.getLauncherName(sl));
+                if (f.equals(secondaryLauncherFile)) {
+                    if (SHORTCUT_HINT.fetchFrom(sl)) {
+                        out.println(prefix
+                                + "  <Shortcut Id=\"desktopShortcut"
+                                + i + "\" Directory=\"DesktopFolder\""
+                                + " Name=\"" + APP_NAME.fetchFrom(sl)
+                                + "\" WorkingDirectory=\"INSTALLDIR\""
+                                + " Advertise=\"no\" Icon=\"Launcher"
+                                + i + ".exe\" IconIndex=\"0\" />");
+                    }
+                    if (MENU_HINT.fetchFrom(sl)) {
+                        out.println(prefix
+                                + "     <Shortcut Id=\"ExeShortcut"
+                                + i + "\" Directory=\"ProgramMenuDir\""
+                                + " Name=\"" + APP_NAME.fetchFrom(sl)
+                                + "\" Advertise=\"no\" Icon=\"Launcher"
+                                + i + ".exe\" IconIndex=\"0\" />");
+                        // Should we allow different menu groups?  Not for now.
+                    }
+                }
+            }
+            out.println(prefix + "   </File>");
+        }
+
+        if (launcherSet) {
+            List<Map<String, ? super Object>> fileAssociations =
+                FILE_ASSOCIATIONS.fetchFrom(params);
+            String regName = APP_REGISTRY_NAME.fetchFrom(params);
+            Set<String> defaultedMimes = new TreeSet<>();
+            int count = 0;
+            for (Map<String, ? super Object> fa : fileAssociations) {
+                String description = FA_DESCRIPTION.fetchFrom(fa);
+                List<String> extensions = FA_EXTENSIONS.fetchFrom(fa);
+                List<String> mimeTypes = FA_CONTENT_TYPE.fetchFrom(fa);
+                File icon = FA_ICON.fetchFrom(fa); // TODO FA_ICON_ICO
+
+                String mime = (mimeTypes == null ||
+                    mimeTypes.isEmpty()) ? null : mimeTypes.get(0);
+
+                if (extensions == null) {
+                    Log.verbose(I18N.getString(
+                          "message.creating-association-with-null-extension"));
+
+                    String entryName = regName + "File";
+                    if (count > 0) {
+                        entryName += "." + count;
+                    }
+                    count++;
+                    out.print(prefix + "   <ProgId Id='" + entryName
+                            + "' Description='" + description + "'");
+                    if (icon != null && icon.exists()) {
+                        out.print(" Icon='" + idToFileMap.get(icon.getName())
+                                + "' IconIndex='0'");
+                    }
+                    out.println(" />");
+                } else {
+                    for (String ext : extensions) {
+                        String entryName = regName + "File";
+                        if (count > 0) {
+                            entryName += "." + count;
+                        }
+                        count++;
+
+                        out.print(prefix + "   <ProgId Id='" + entryName
+                                + "' Description='" + description + "'");
+                        if (icon != null && icon.exists()) {
+                            out.print(" Icon='"
+                                    + idToFileMap.get(icon.getName())
+                                    + "' IconIndex='0'");
+                        }
+                        out.println(">");
+
+                        if (extensions == null) {
+                            Log.verbose(I18N.getString(
+                            "message.creating-association-with-null-extension"));
+                        } else {
+                            out.print(prefix + "    <Extension Id='"
+                                    + ext + "' Advertise='no'");
+                            if (mime == null) {
+                                out.println(">");
+                            } else {
+                                out.println(" ContentType='" + mime + "'>");
+                                if (!defaultedMimes.contains(mime)) {
+                                    out.println(prefix
+                                            + "      <MIME ContentType='"
+                                            + mime + "' Default='yes' />");
+                                    defaultedMimes.add(mime);
+                                }
+                            }
+                            out.println(prefix
+                                    + "      <Verb Id='open' Command='Open' "
+                                    + "TargetFile='" + LAUNCHER_ID
+                                    + "' Argument='\"%1\"' />");
+                            out.println(prefix + "    </Extension>");
+                        }
+                        out.println(prefix + "   </ProgId>");
+                    }
+                }
+            }
+        }
+
+        out.println(prefix + " </Component>");
+
+        for (File d : dirs) {
+            out.println(prefix + " <Directory Id=\"dirid" + (id++)
+                    + "\" Name=\"" + d.getName() + "\">");
+            walkFileTree(params, d, out, prefix + " ");
+            out.println(prefix + " </Directory>");
+        }
+    }
+
+    String getRegistryRoot(Map<String, ? super Object> params) {
+        if (MSI_SYSTEM_WIDE.fetchFrom(params)) {
+            return "HKLM";
+        } else {
+            return "HKCU";
+        }
+    }
+
+    boolean prepareContentList(Map<String, ? super Object> params)
+            throws FileNotFoundException {
+        File f = new File(
+                CONFIG_ROOT.fetchFrom(params), MSI_PROJECT_CONTENT_FILE);
+        PrintStream out = new PrintStream(f);
+
+        // opening
+        out.println("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>");
+        out.println("<Include>");
+
+        out.println(" <Directory Id=\"TARGETDIR\" Name=\"SourceDir\">");
+        if (MSI_SYSTEM_WIDE.fetchFrom(params)) {
+            // install to programfiles
+            out.println("  <Directory Id=\"ProgramFiles64Folder\" "
+                        + "Name=\"PFiles\">");
+        } else {
+            // install to user folder
+            out.println(
+                    "  <Directory Name=\"AppData\" Id=\"LocalAppDataFolder\">");
+        }
+        out.println("   <Directory Id=\"APPLICATIONFOLDER\" Name=\""
+                + APP_NAME.fetchFrom(params) + "\">");
+
+        // dynamic part
+        id = 0;
+        compId = 0; // reset counters
+        walkFileTree(params, WIN_APP_IMAGE.fetchFrom(params), out, "    ");
+
+        // closing
+        out.println("   </Directory>");
+        out.println("  </Directory>");
+
+        // for shortcuts
+        if (SHORTCUT_HINT.fetchFrom(params)) {
+            out.println("  <Directory Id=\"DesktopFolder\" />");
+        }
+        if (MENU_HINT.fetchFrom(params)) {
+            out.println("  <Directory Id=\"ProgramMenuFolder\">");
+            out.println("    <Directory Id=\"ProgramMenuDir\" Name=\""
+                    + MENU_GROUP.fetchFrom(params) + "\">");
+            out.println("      <Component Id=\"comp" + (compId++) + "\""
+                    + " Guid=\"" + UUID.randomUUID().toString() + "\""
+                    + " Win64=\"yes\""
+                    + ">");
+            out.println("        <RemoveFolder Id=\"ProgramMenuDir\" "
+                    + "On=\"uninstall\" />");
+            // This has to be under HKCU to make WiX happy.
+            // There are numberous discussions on this amoung WiX users
+            // (if user A installs and user B uninstalls key is left behind)
+            // there are suggested workarounds but none of them are appealing.
+            // Leave it for now
+            out.println(
+                    "         <RegistryValue Root=\"HKCU\" Key=\"Software\\"
+                    + VENDOR.fetchFrom(params) + "\\"
+                    + APP_NAME.fetchFrom(params)
+                    + "\" Type=\"string\" Value=\"\" />");
+            out.println("      </Component>");
+            out.println("    </Directory>");
+            out.println(" </Directory>");
+        }
+
+        out.println(" </Directory>");
+
+        out.println(" <Feature Id=\"DefaultFeature\" "
+                + "Title=\"Main Feature\" Level=\"1\">");
+        for (int j = 0; j < compId; j++) {
+            out.println("    <ComponentRef Id=\"comp" + j + "\" />");
+        }
+        // component is defined in the template.wsx
+        out.println("    <ComponentRef Id=\"CleanupMainApplicationFolder\" />");
+        out.println(" </Feature>");
+        out.println("</Include>");
+
+        out.close();
+        return true;
+    }
+
+    private File getConfig_ProjectFile(Map<String, ? super Object> params) {
+        return new File(CONFIG_ROOT.fetchFrom(params),
+                APP_NAME.fetchFrom(params) + ".wxs");
+    }
+
+    private String getLicenseFile(Map<String, ? super Object> p) {
+        String licenseFile = LICENSE_FILE.fetchFrom(p);
+        if (licenseFile != null) {
+            File lfile = new File(licenseFile);
+            File destFile = new File(CONFIG_ROOT.fetchFrom(p), lfile.getName());
+            String filePath = destFile.getAbsolutePath();
+            if (filePath.contains(" ")) {
+                return "\"" + filePath + "\"";
+            } else {
+                return filePath;
+            }
+        }
+
+        return null;
+    }
+
+    private boolean prepareWiXConfig(
+            Map<String, ? super Object> params) throws IOException {
+        return prepareMainProjectFile(params) && prepareContentList(params);
+
+    }
+    private final static String MSI_PROJECT_TEMPLATE = "template.wxs";
+    private final static String MSI_PROJECT_TEMPLATE_SERVER_JRE =
+            "template.jre.wxs";
+    private final static String MSI_PROJECT_CONTENT_FILE = "bundle.wxi";
+
+    private File buildMSI(Map<String, ? super Object> params, File outdir)
+            throws IOException {
+        File tmpDir = new File(BUILD_ROOT.fetchFrom(params), "tmp");
+        File candleOut = new File(
+                tmpDir, APP_NAME.fetchFrom(params) +".wixobj");
+        File msiOut = new File(
+                outdir, INSTALLER_FILE_NAME.fetchFrom(params) + ".msi");
+
+        Log.verbose(MessageFormat.format(I18N.getString(
+                "message.preparing-msi-config"), msiOut.getAbsolutePath()));
+
+        msiOut.getParentFile().mkdirs();
+
+        // run candle
+        ProcessBuilder pb = new ProcessBuilder(
+                TOOL_CANDLE_EXECUTABLE.fetchFrom(params),
+                "-nologo",
+                getConfig_ProjectFile(params).getAbsolutePath(),
+                "-ext", "WixUtilExtension",
+                "-out", candleOut.getAbsolutePath());
+        pb = pb.directory(WIN_APP_IMAGE.fetchFrom(params));
+        IOUtils.exec(pb, false);
+
+        Log.verbose(MessageFormat.format(I18N.getString(
+                "message.generating-msi"), msiOut.getAbsolutePath()));
+
+        boolean enableLicenseUI = (getLicenseFile(params) != null);
+        boolean enableInstalldirUI = INSTALLDIR_CHOOSER.fetchFrom(params);
+
+        List<String> commandLine = new ArrayList<>();
+
+        commandLine.add(TOOL_LIGHT_EXECUTABLE.fetchFrom(params));
+        if (enableLicenseUI) {
+            commandLine.add("-dWixUILicenseRtf="+getLicenseFile(params));
+        }
+        commandLine.add("-nologo");
+        commandLine.add("-spdb");
+        commandLine.add("-sice:60");
+                // ignore warnings due to "missing launcguage info" (ICE60)
+        commandLine.add(candleOut.getAbsolutePath());
+        commandLine.add("-ext");
+        commandLine.add("WixUtilExtension");
+        if (enableLicenseUI || enableInstalldirUI) {
+            commandLine.add("-ext");
+            commandLine.add("WixUIExtension.dll");
+        }
+        commandLine.add("-out");
+        commandLine.add(msiOut.getAbsolutePath());
+
+        // create .msi
+        pb = new ProcessBuilder(commandLine);
+
+        pb = pb.directory(WIN_APP_IMAGE.fetchFrom(params));
+        IOUtils.exec(pb, false);
+
+        candleOut.delete();
+        IOUtils.deleteRecursive(tmpDir);
+
+        return msiOut;
+    }
+
+    public static void ensureByMutationFileIsRTF(File f) {
+        if (f == null || !f.isFile()) return;
+
+        try {
+            boolean existingLicenseIsRTF = false;
+
+            try (FileInputStream fin = new FileInputStream(f)) {
+                byte[] firstBits = new byte[7];
+
+                if (fin.read(firstBits) == firstBits.length) {
+                    String header = new String(firstBits);
+                    existingLicenseIsRTF = "{\\rtf1\\".equals(header);
+                }
+            }
+
+            if (!existingLicenseIsRTF) {
+                List<String> oldLicense = Files.readAllLines(f.toPath());
+                try (Writer w = Files.newBufferedWriter(
+                        f.toPath(), Charset.forName("Windows-1252"))) {
+                    w.write("{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang1033"
+                            + "{\\fonttbl{\\f0\\fnil\\fcharset0 Arial;}}\n"
+                            + "\\viewkind4\\uc1\\pard\\sa200\\sl276"
+                            + "\\slmult1\\lang9\\fs20 ");
+                    oldLicense.forEach(l -> {
+                        try {
+                            for (char c : l.toCharArray()) {
+                                // 0x00 <= ch < 0x20 Escaped (\'hh)
+                                // 0x20 <= ch < 0x80 Raw(non - escaped) char
+                                // 0x80 <= ch <= 0xFF Escaped(\ 'hh)
+                                // 0x5C, 0x7B, 0x7D (special RTF characters
+                                // \,{,})Escaped(\'hh)
+                                // ch > 0xff Escaped (\\ud###?)
+                                if (c < 0x10) {
+                                    w.write("\\'0");
+                                    w.write(Integer.toHexString(c));
+                                } else if (c > 0xff) {
+                                    w.write("\\ud");
+                                    w.write(Integer.toString(c));
+                                    // \\uc1 is in the header and in effect
+                                    // so we trail with a replacement char if
+                                    // the font lacks that character - '?'
+                                    w.write("?");
+                                } else if ((c < 0x20) || (c >= 0x80) ||
+                                        (c == 0x5C) || (c == 0x7B) ||
+                                        (c == 0x7D)) {
+                                    w.write("\\'");
+                                    w.write(Integer.toHexString(c));
+                                } else {
+                                    w.write(c);
+                                }
+                            }
+                            // blank lines are interpreted as paragraph breaks
+                            if (l.length() < 1) {
+                                w.write("\\par");
+                            } else {
+                                w.write(" ");
+                            }
+                            w.write("\r\n");
+                        } catch (IOException e) {
+                            Log.verbose(e);
+                        }
+                    });
+                    w.write("}\r\n");
+                }
+            }
+        } catch (IOException e) {
+            Log.verbose(e);
+        }
+
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WindowsAppImageBuilder.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,449 @@
+/*
+ * Copyright (c) 2015, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jpackage.internal;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.UncheckedIOException;
+import java.io.Writer;
+import java.io.BufferedWriter;
+import java.io.FileWriter;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+import java.nio.file.attribute.PosixFilePermission;
+import java.text.MessageFormat;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.ResourceBundle;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.regex.Pattern;
+import java.util.stream.Stream;
+
+import static jdk.jpackage.internal.StandardBundlerParam.*;
+
+public class WindowsAppImageBuilder extends AbstractAppImageBuilder {
+
+    static {
+        System.loadLibrary("jpackage");
+    }
+
+    private static final ResourceBundle I18N = ResourceBundle.getBundle(
+            "jdk.jpackage.internal.resources.WinResources");
+
+    private final static String LIBRARY_NAME = "applauncher.dll";
+    private final static String REDIST_MSVCR = "vcruntimeVS_VER.dll";
+    private final static String REDIST_MSVCP = "msvcpVS_VER.dll";
+
+    private final static String TEMPLATE_APP_ICON ="javalogo_white_48.ico";
+
+    private static final String EXECUTABLE_PROPERTIES_TEMPLATE =
+            "WinLauncher.template";
+
+    private final Path root;
+    private final Path appDir;
+    private final Path appModsDir;
+    private final Path runtimeDir;
+    private final Path mdir;
+
+    private final Map<String, ? super Object> params;
+
+    public static final BundlerParamInfo<Boolean> REBRAND_EXECUTABLE =
+            new WindowsBundlerParam<>(
+            I18N.getString("param.rebrand-executable.name"),
+            I18N.getString("param.rebrand-executable.description"),
+            "win.launcher.rebrand",
+            Boolean.class,
+            params -> Boolean.TRUE,
+            (s, p) -> Boolean.valueOf(s));
+
+    public static final BundlerParamInfo<File> ICON_ICO =
+            new StandardBundlerParam<>(
+            I18N.getString("param.icon-ico.name"),
+            I18N.getString("param.icon-ico.description"),
+            "icon.ico",
+            File.class,
+            params -> {
+                File f = ICON.fetchFrom(params);
+                if (f != null && !f.getName().toLowerCase().endsWith(".ico")) {
+                    Log.error(MessageFormat.format(
+                            I18N.getString("message.icon-not-ico"), f));
+                    return null;
+                }
+                return f;
+            },
+            (s, p) -> new File(s));
+
+    public static final StandardBundlerParam<Boolean> CONSOLE_HINT =
+            new WindowsBundlerParam<>(
+            I18N.getString("param.console-hint.name"),
+            I18N.getString("param.console-hint.description"),
+            Arguments.CLIOptions.WIN_CONSOLE_HINT.getId(),
+            Boolean.class,
+            params -> false,
+            // valueOf(null) is false,
+            // and we actually do want null in some cases
+            (s, p) -> (s == null
+            || "null".equalsIgnoreCase(s)) ? true : Boolean.valueOf(s));
+
+    public WindowsAppImageBuilder(Map<String, Object> config, Path imageOutDir)
+            throws IOException {
+        super(config,
+                imageOutDir.resolve(APP_NAME.fetchFrom(config) + "/runtime"));
+
+        Objects.requireNonNull(imageOutDir);
+
+        this.params = config;
+
+        this.root = imageOutDir.resolve(APP_NAME.fetchFrom(params));
+        this.appDir = root.resolve("app");
+        this.appModsDir = appDir.resolve("mods");
+        this.runtimeDir = root.resolve("runtime");
+        this.mdir = runtimeDir.resolve("lib");
+        Files.createDirectories(appDir);
+        Files.createDirectories(runtimeDir);
+    }
+
+    public WindowsAppImageBuilder(String jreName, Path imageOutDir)
+            throws IOException {
+        super(null, imageOutDir.resolve(jreName));
+
+        Objects.requireNonNull(imageOutDir);
+
+        this.params = null;
+        this.root = imageOutDir.resolve(jreName);
+        this.appDir = null;
+        this.appModsDir = null;
+        this.runtimeDir = root;
+        this.mdir = runtimeDir.resolve("lib");
+        Files.createDirectories(runtimeDir);
+    }
+
+    private Path destFile(String dir, String filename) {
+        return runtimeDir.resolve(dir).resolve(filename);
+    }
+
+    private void writeEntry(InputStream in, Path dstFile) throws IOException {
+        Files.createDirectories(dstFile.getParent());
+        Files.copy(in, dstFile);
+    }
+
+    private void writeSymEntry(Path dstFile, Path target) throws IOException {
+        Files.createDirectories(dstFile.getParent());
+        Files.createLink(dstFile, target);
+    }
+
+    /**
+     * chmod ugo+x file
+     */
+    private void setExecutable(Path file) {
+        try {
+            Set<PosixFilePermission> perms =
+                Files.getPosixFilePermissions(file);
+            perms.add(PosixFilePermission.OWNER_EXECUTE);
+            perms.add(PosixFilePermission.GROUP_EXECUTE);
+            perms.add(PosixFilePermission.OTHERS_EXECUTE);
+            Files.setPosixFilePermissions(file, perms);
+        } catch (IOException ioe) {
+            throw new UncheckedIOException(ioe);
+        }
+    }
+
+    private static void createUtf8File(File file, String content)
+            throws IOException {
+        try (OutputStream fout = new FileOutputStream(file);
+             Writer output = new OutputStreamWriter(fout, "UTF-8")) {
+            output.write(content);
+        }
+    }
+
+    public static String getLauncherName(Map<String, ? super Object> p) {
+        return APP_NAME.fetchFrom(p) + ".exe";
+    }
+
+    // Returns launcher resource name for launcher we need to use.
+    public static String getLauncherResourceName(
+            Map<String, ? super Object> p) {
+        if (CONSOLE_HINT.fetchFrom(p)) {
+            return "jpackageapplauncher.exe";
+        } else {
+            return "jpackageapplauncherw.exe";
+        }
+    }
+
+    public static String getLauncherCfgName(Map<String, ? super Object> p) {
+        return "app/" + APP_NAME.fetchFrom(p) +".cfg";
+    }
+
+    private File getConfig_AppIcon(Map<String, ? super Object> params) {
+        return new File(getConfigRoot(params),
+                APP_NAME.fetchFrom(params) + ".ico");
+    }
+
+    private File getConfig_ExecutableProperties(
+           Map<String, ? super Object> params) {
+        return new File(getConfigRoot(params),
+                APP_NAME.fetchFrom(params) + ".properties");
+    }
+
+    File getConfigRoot(Map<String, ? super Object> params) {
+        return CONFIG_ROOT.fetchFrom(params);
+    }
+
+    @Override
+    public Path getAppDir() {
+        return appDir;
+    }
+
+    @Override
+    public Path getAppModsDir() {
+        return appModsDir;
+    }
+
+    @Override
+    public void prepareApplicationFiles() throws IOException {
+        Map<String, ? super Object> originalParams = new HashMap<>(params);
+        File rootFile = root.toFile();
+        if (!rootFile.isDirectory() && !rootFile.mkdirs()) {
+            throw new RuntimeException(MessageFormat.format(I18N.getString(
+                "error.cannot-create-output-dir"), rootFile.getAbsolutePath()));
+        }
+        if (!rootFile.canWrite()) {
+            throw new RuntimeException(MessageFormat.format(
+                    I18N.getString("error.cannot-write-to-output-dir"),
+                    rootFile.getAbsolutePath()));
+        }
+        // create the .exe launchers
+        createLauncherForEntryPoint(params);
+
+        // copy the jars
+        copyApplication(params);
+
+        // copy in the needed libraries
+        try (InputStream is_lib = getResourceAsStream(LIBRARY_NAME)) {
+            Files.copy(is_lib, root.resolve(LIBRARY_NAME));
+        }
+
+        copyMSVCDLLs();
+
+        // create the secondary launchers, if any
+        List<Map<String, ? super Object>> entryPoints =
+                StandardBundlerParam.SECONDARY_LAUNCHERS.fetchFrom(params);
+        for (Map<String, ? super Object> entryPoint : entryPoints) {
+            Map<String, ? super Object> tmp = new HashMap<>(originalParams);
+            tmp.putAll(entryPoint);
+            createLauncherForEntryPoint(tmp);
+        }
+    }
+
+    @Override
+    public void prepareJreFiles() throws IOException {}
+
+    private void copyMSVCDLLs() throws IOException {
+        AtomicReference<IOException> ioe = new AtomicReference<>();
+        try (Stream<Path> files = Files.list(runtimeDir.resolve("bin"))) {
+            files.filter(p -> Pattern.matches(
+                    "^(vcruntime|msvcp|msvcr|ucrtbase|api-ms-win-).*\\.dll$",
+                    p.toFile().getName().toLowerCase()))
+                 .forEach(p -> {
+                    try {
+                        Files.copy(p, root.resolve((p.toFile().getName())));
+                    } catch (IOException e) {
+                        ioe.set(e);
+                    }
+                });
+        }
+
+        IOException e = ioe.get();
+        if (e != null) {
+            throw e;
+        }
+    }
+
+    // TODO: do we still need this?
+    private boolean copyMSVCDLLs(String VS_VER) throws IOException {
+        final InputStream REDIST_MSVCR_URL = getResourceAsStream(
+                REDIST_MSVCR.replaceAll("VS_VER", VS_VER));
+        final InputStream REDIST_MSVCP_URL = getResourceAsStream(
+                REDIST_MSVCP.replaceAll("VS_VER", VS_VER));
+
+        if (REDIST_MSVCR_URL != null && REDIST_MSVCP_URL != null) {
+            Files.copy(
+                    REDIST_MSVCR_URL,
+                    root.resolve(REDIST_MSVCR.replaceAll("VS_VER", VS_VER)));
+            Files.copy(
+                    REDIST_MSVCP_URL,
+                    root.resolve(REDIST_MSVCP.replaceAll("VS_VER", VS_VER)));
+            return true;
+        }
+
+        return false;
+    }
+
+    private void validateValueAndPut(
+            Map<String, String> data, String key,
+            BundlerParamInfo<String> param,
+            Map<String, ? super Object> params) {
+        String value = param.fetchFrom(params);
+        if (value.contains("\r") || value.contains("\n")) {
+            Log.error("Configuration Parameter " + param.getID()
+                    + " contains multiple lines of text, ignore it");
+            data.put(key, "");
+            return;
+        }
+        data.put(key, value);
+    }
+
+    protected void prepareExecutableProperties(
+           Map<String, ? super Object> params) throws IOException {
+        Map<String, String> data = new HashMap<>();
+
+        // mapping Java parameters in strings for version resource
+        validateValueAndPut(data, "COMPANY_NAME", VENDOR, params);
+        validateValueAndPut(data, "FILE_DESCRIPTION", DESCRIPTION, params);
+        validateValueAndPut(data, "FILE_VERSION", VERSION, params);
+        data.put("INTERNAL_NAME", getLauncherName(params));
+        validateValueAndPut(data, "LEGAL_COPYRIGHT", COPYRIGHT, params);
+        data.put("ORIGINAL_FILENAME", getLauncherName(params));
+        validateValueAndPut(data, "PRODUCT_NAME", APP_NAME, params);
+        validateValueAndPut(data, "PRODUCT_VERSION", VERSION, params);
+
+        try (Writer w = new BufferedWriter(
+                new FileWriter(getConfig_ExecutableProperties(params)))) {
+            String content = preprocessTextResource(
+                    getConfig_ExecutableProperties(params).getName(),
+                    I18N.getString("resource.executable-properties-template"),
+                    EXECUTABLE_PROPERTIES_TEMPLATE, data,
+                    VERBOSE.fetchFrom(params),
+                    RESOURCE_DIR.fetchFrom(params));
+            w.write(content);
+        }
+    }
+
+    private void createLauncherForEntryPoint(
+            Map<String, ? super Object> p) throws IOException {
+
+        File launcherIcon = ICON_ICO.fetchFrom(p);
+        File icon = launcherIcon != null ?
+                launcherIcon : ICON_ICO.fetchFrom(params);
+        File iconTarget = getConfig_AppIcon(p);
+
+        InputStream in = locateResource(
+                APP_NAME.fetchFrom(params) + ".ico",
+                "icon",
+                TEMPLATE_APP_ICON,
+                icon,
+                VERBOSE.fetchFrom(params),
+                RESOURCE_DIR.fetchFrom(params));
+
+        Files.copy(in, iconTarget.toPath(),
+                StandardCopyOption.REPLACE_EXISTING);
+
+        writeCfgFile(p, root.resolve(
+                getLauncherCfgName(p)).toFile(), "$APPDIR\\runtime");
+
+        prepareExecutableProperties(p);
+
+        // Copy executable root folder
+        Path executableFile = root.resolve(getLauncherName(p));
+        try (InputStream is_launcher =
+                getResourceAsStream(getLauncherResourceName(p))) {
+            writeEntry(is_launcher, executableFile);
+        }
+
+        File launcher = executableFile.toFile();
+        launcher.setWritable(true, true);
+
+        // Update branding of EXE file
+        if (REBRAND_EXECUTABLE.fetchFrom(p)) {
+            try {
+                String tempDirectory = WindowsDefender.getUserTempDirectory();
+                if (Arguments.CLIOptions.context().userProvidedBuildRoot) {
+                    tempDirectory = BUILD_ROOT.fetchFrom(p).getAbsolutePath();
+                }
+                if (WindowsDefender.isThereAPotentialWindowsDefenderIssue(
+                        tempDirectory)) {
+                    Log.error(MessageFormat.format(I18N.getString(
+                            "message.potential.windows.defender.issue"),
+                            tempDirectory));
+                }
+
+                launcher.setWritable(true);
+
+                if (iconTarget.exists()) {
+                    iconSwap(iconTarget.getAbsolutePath(),
+                            launcher.getAbsolutePath());
+                }
+
+                File executableProperties = getConfig_ExecutableProperties(p);
+
+                if (executableProperties.exists()) {
+                    if (versionSwap(executableProperties.getAbsolutePath(),
+                            launcher.getAbsolutePath()) != 0) {
+                        throw new RuntimeException(MessageFormat.format(
+                                I18N.getString("error.version-swap"),
+                                executableProperties.getAbsolutePath()));
+                    }
+                }
+            } finally {
+                executableFile.toFile().setReadOnly();
+            }
+        }
+
+        Files.copy(iconTarget.toPath(),
+                root.resolve(APP_NAME.fetchFrom(p) + ".ico"));
+    }
+
+    private void copyApplication(Map<String, ? super Object> params)
+            throws IOException {
+        List<RelativeFileSet> appResourcesList =
+                APP_RESOURCES_LIST.fetchFrom(params);
+        if (appResourcesList == null) {
+            throw new RuntimeException("Null app resources?");
+        }
+        for (RelativeFileSet appResources : appResourcesList) {
+            if (appResources == null) {
+                throw new RuntimeException("Null app resources?");
+            }
+            File srcdir = appResources.getBaseDirectory();
+            for (String fname : appResources.getIncludedFiles()) {
+                copyEntry(appDir, srcdir, fname);
+            }
+        }
+    }
+
+    private static native int iconSwap(String iconTarget, String launcher);
+
+    private static native int versionSwap(String executableProperties, String launcher);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WindowsBundlerParam.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2014, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jpackage.internal;
+
+import java.util.Map;
+import java.util.ResourceBundle;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+
+class WindowsBundlerParam<T> extends StandardBundlerParam<T> {
+
+    private static final ResourceBundle I18N = ResourceBundle.getBundle(
+            "jdk.jpackage.internal.resources.WinResources");
+
+    WindowsBundlerParam(String name, String description, String id,
+            Class<T> valueType,
+            Function<Map<String, ? super Object>, T> defaultValueFunction,
+            BiFunction<String,
+            Map<String, ? super Object>, T> stringConverter) {
+        super(name, description, id, valueType,
+                defaultValueFunction, stringConverter);
+    }
+
+    static final BundlerParamInfo<String> INSTALLER_FILE_NAME =
+            new StandardBundlerParam<> (
+            I18N.getString("param.installer-name.name"),
+            I18N.getString("param.installer-name.description"),
+            "win.installerName",
+            String.class,
+            params -> {
+                String nm = APP_NAME.fetchFrom(params);
+                if (nm == null) return null;
+
+                String version = VERSION.fetchFrom(params);
+                if (version == null) {
+                    return nm;
+                } else {
+                    return nm + "-" + version;
+                }
+            },
+            (s, p) -> s);
+
+    static final BundlerParamInfo<String> APP_REGISTRY_NAME =
+            new StandardBundlerParam<> (
+            I18N.getString("param.registry-name.name"),
+            I18N.getString("param.registry-name.description"),
+            Arguments.CLIOptions.WIN_REGISTRY_NAME.getId(),
+            String.class,
+            params -> {
+                String nm = APP_NAME.fetchFrom(params);
+                if (nm == null) return null;
+
+                return nm.replaceAll("[^-a-zA-Z\\.0-9]", "");
+            },
+            (s, p) -> s);
+
+    static final StandardBundlerParam<String> MENU_GROUP =
+            new StandardBundlerParam<>(
+                    I18N.getString("param.menu-group.name"),
+                    I18N.getString("param.menu-group.description"),
+                    Arguments.CLIOptions.WIN_MENU_GROUP.getId(),
+                    String.class,
+                    params -> params.containsKey(VENDOR.getID())
+                            ? VENDOR.fetchFrom(params)
+                            : params.containsKey(CATEGORY.getID())
+                            ? CATEGORY.fetchFrom(params)
+                            : I18N.getString("param.menu-group.default"),
+                    (s, p) -> s
+            );
+
+    static final BundlerParamInfo<Boolean> INSTALLDIR_CHOOSER =
+            new StandardBundlerParam<> (
+            I18N.getString("param.installdir-chooser.name"),
+            I18N.getString("param.installdir-chooser.description"),
+            Arguments.CLIOptions.WIN_DIR_CHOOSER.getId(),
+            Boolean.class,
+            params -> Boolean.FALSE,
+            (s, p) -> Boolean.valueOf(s)
+    );
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WindowsDefender.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2012, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jpackage.internal;
+
+import java.util.List;
+
+final class WindowsDefender {
+
+    private WindowsDefender() {}
+
+    static final boolean isThereAPotentialWindowsDefenderIssue(String dir) {
+        boolean result = false;
+
+        if (Platform.getPlatform() == Platform.WINDOWS &&
+            Platform.getMajorVersion() == 10) {
+
+            // If DisableRealtimeMonitoring is not enabled then there
+            // may be a problem.
+            if (!WindowsRegistry.readDisableRealtimeMonitoring() &&
+                !isDirectoryInExclusionPath(dir)) {
+                result = true;
+            }
+        }
+
+        return result;
+    }
+
+    private static boolean isDirectoryInExclusionPath(String dir) {
+        boolean result = false;
+        // If the user temp directory is not found in the exclusion
+        // list then there may be a problem.
+        List<String> paths = WindowsRegistry.readExclusionsPaths();
+        for (String s : paths) {
+            if (WindowsRegistry.comparePaths(s, dir)) {
+                result = true;
+                break;
+            }
+        }
+
+        return result;
+    }
+
+    static final String getUserTempDirectory() {
+        String tempDirectory = System.getProperty("java.io.tmpdir");
+        return tempDirectory;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WindowsRegistry.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2012, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jpackage.internal;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import static jdk.jpackage.internal.IOUtils.exec;
+
+final class WindowsRegistry {
+
+    // Currently we only support HKEY_LOCAL_MACHINE. Native implementation will
+    // require support for additinal HKEY if needed.
+    private static final int HKEY_LOCAL_MACHINE = 1;
+
+    static {
+        System.loadLibrary("jpackage");
+    }
+
+    private WindowsRegistry() {}
+
+    /**
+     * Reads the registry value for DisableRealtimeMonitoring.
+     * @return true if DisableRealtimeMonitoring is set to 0x1,
+     *         false otherwise.
+     */
+    static final boolean readDisableRealtimeMonitoring() {
+        final String subKey = "Software\\Microsoft\\"
+                  + "Windows Defender\\Real-Time Protection";
+        final String value = "DisableRealtimeMonitoring";
+        int result = readDwordValue(HKEY_LOCAL_MACHINE, subKey, value, 0);
+        return (result == 1);
+    }
+
+    static final List<String> readExclusionsPaths() {
+        List<String> result = new ArrayList<>();
+        final String subKey = "Software\\Microsoft\\"
+                + "Windows Defender\\Exclusions\\Paths";
+        long lKey = openRegistryKey(HKEY_LOCAL_MACHINE, subKey);
+        if (lKey == 0) {
+            return result;
+        }
+
+        String valueName;
+        int index = 0;
+        do {
+            valueName = enumRegistryValue(lKey, index);
+            if (valueName != null) {
+                result.add(valueName);
+                index++;
+            }
+        } while (valueName != null);
+
+        closeRegistryKey(lKey);
+
+        return result;
+    }
+
+    /**
+     * Reads DWORD registry value.
+     *
+     * @param key one of HKEY predefine value
+     * @param subKey registry sub key
+     * @param value value to read
+     * @param defaultValue default value in case if subKey or value not found
+     *                     or any other errors occurred
+     * @return value's data only if it was read successfully, otherwise
+     *         defaultValue
+     */
+    private static native int readDwordValue(int key, String subKey,
+            String value, int defaultValue);
+
+    /**
+     * Open registry key.
+     *
+     * @param key one of HKEY predefine value
+     * @param subKey registry sub key
+     * @return native handle to open key
+     */
+    private static native long openRegistryKey(int key, String subKey);
+
+    /**
+     * Enumerates the values for registry key.
+     *
+     * @param lKey native handle to open key returned by openRegistryKey
+     * @param index index of value starting from 0. Increment until this
+     *              function returns NULL which means no more values.
+     * @return returns value or NULL if error or no more data
+     */
+    private static native String enumRegistryValue(long lKey, int index);
+
+    /**
+     * Close registry key.
+     *
+     * @param lKey native handle to open key returned by openRegistryKey
+     */
+    private static native void closeRegistryKey(long lKey);
+
+    /**
+     * Compares two Windows paths regardless case and if paths are short or long.
+     *
+     * @param path1 path to compare
+     * @param path2 path to compare
+     * @return true if paths point to same location
+     */
+    public static native boolean comparePaths(String path1, String path2);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinLauncher.template	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,34 @@
+#
+# Copyright (c) 2017, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+# particular file as subject to the "Classpath" exception as provided
+# by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+# or visit www.oracle.com if you need additional information or have any
+# questions.
+#
+#
+
+CompanyName=COMPANY_NAME
+FileDescription=FILE_DESCRIPTION
+FileVersion=FILE_VERSION
+InternalName=INTERNAL_NAME
+LegalCopyright=LEGAL_COPYRIGHT
+OriginalFilename=ORIGINAL_FILENAME
+ProductName=PRODUCT_NAME
+ProductVersion=PRODUCT_VERSION
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources.properties	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,132 @@
+#
+# Copyright (c) 2017, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+# particular file as subject to the "Classpath" exception as provided
+# by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+# or visit www.oracle.com if you need additional information or have any
+# questions.
+#
+#
+
+app.bundler.name=Windows Application Image
+app.bundler.description=A Directory based image of a windows Application with an optionally co-bundled JRE.  Used as a base for the Installer bundlers
+exe.bundler.name=EXE Installer
+exe.bundler.description=Microsoft Windows EXE Installer, via InnoIDE.
+msi.bundler.name=MSI Installer
+msi.bundler.description=Microsoft Windows MSI Installer, via WiX.
+
+param.raw-executable-url.name=Launcher URL
+param.raw-executable-url.description=Override the jpackage default launcher with a custom launcher.
+param.rebrand-executable.name=Rebrand Launcher
+param.rebrand-executable.description=Update the launcher with the application icon and update ownership information.
+param.icon-ico.name=.ico Icon
+param.icon-ico.description=Icon for the application, in ICO format.
+param.console-hint.name=Console Hint
+param.console-hint.description=Indicates if the bundler should use console launcher
+param.menu-group.name=Menu Group
+param.menu-group.description=The Start Menu group this application should be placed in
+param.menu-group.default=Unknown
+param.installer-name.name=Installer Name
+param.installer-name.description=The filename of the generated installer without the file type extension.  Default is <App Name>-<Version>.
+param.registry-name.name=Registry Name
+param.registry-name.description=The name of the application for registry references.  Default is the Application Name with only alphanumerics, dots, and dashes (no whitespace).
+param.installdir-chooser.name=Install Directory Chooser
+param.installdir-chooser.description=Adds a dialog to let the user choose a directory where the product will be installed.
+param.system-wide.name=System Wide
+param.system-wide.description=Should this application attempt to install itself system wide, or only for each user?  Null means use the system default.
+param.exe-bundler.name=Exe Installer Bundler
+param.exe-bundler.description=Exe Installer Bundler
+param.image-dir.name=Image Dir
+param.image-dir.description=Image Dir
+param.app-dir.name=App Dir
+param.app-dir.description=App Dir
+param.menu-shortcut-hint.name=Menu Hint
+param.menu-shortcut-hint.description=If the bundler can add the application to the system menu, should it?
+param.desktop-shortcut-hint.name=Shortcut Hint
+param.desktop-shortcut-hint.description=If the bundler can create desktop shortcuts, should it make one?
+param.upgrade-uuid.name=Upgrade UUID
+param.upgrade-uuid.description=The UUID associated with upgrades for this package.
+param.product-version.name=Product Version
+param.product-version.description=The version of the application as seen by Windows and MSI, of the form "1.2.3"
+param.iscc-path.name=InnoSetup iscc.exe location
+param.iscc-path.description=File path to iscc.exe from the InnoSetup tool.
+param.msi-bundler.name=MSI App Bundler
+param.msi-bundler.description=MSI App Bundler
+param.can-use-wix36.name=Can Use Wix
+param.can-use-wix36.description=Can Use Wix
+param.candle-path.name=WiX candle.exe location
+param.candle-path.description=File path to candle.exe from the WiX toolset.
+param.light-path.name=WiX light.exe location
+param.light-path.description=File path to light.exe from the WiX toolset.
+
+resource.application-icon=application icon
+resource.executable-properties-template=Template for creating executable properties file.
+resource.inno-setup-project-file=Inno Setup project file
+resource.setup-icon=setup dialog icon
+resource.post-install-script=script to run after application image is populated
+resource.wix-config-file=WiX config file
+
+error.parameters-null=Parameters map is null.
+error.parameters-null.advice=Pass in a non-null parameters map.
+error.no-windows-resources=This copy of the JDK does not support Windows.
+error.no-windows-resources.advice=Please use the Oracle JDK for Windows.
+error.cannot-find-launcher=Cannot find cfg file in predefined app image directory {0}.
+error.cannot-create-output-dir=Output directory {0} cannot be created.
+error.cannot-write-to-output-dir=Output directory {0} is not writable.
+error.iscc-not-found=Can not find Inno Setup Compiler (iscc.exe).
+error.iscc-not-found.advice=Download Inno Setup 5 or later from http\://www.jrsoftware.org and add it to the PATH.
+error.copyright-is-too-long=The copyright string is too long for InnoSetup.
+error.copyright-is-too-long.advice=Provide a copyright string shorter than 100 characters.
+error.too-many-content-types-for-file-association=More than one MIME types was specified for File Association number {0}.
+error.too-many-content-types-for-file-association.advice=Specify one and only one MIME type for each file association.
+error.no-wix-tools=Can not find WiX tools (light.exe, candle.exe).
+error.no-wix-tools.advice=Download WiX 3.0 or later from http\://wix.sf.net and add it to the PATH.
+error.version-string-wrong-format=Version string is not compatible with MSI rules [{0}].
+error.version-string-wrong-format.advice=Set the bundler argument "{0}" according to these rules: http\://msdn.microsoft.com/en-us/library/aa370859%28v\=VS.85%29.aspx
+error.version-string-major-out-of-range=Major version must be in the range [0, 255]
+error.version-string-build-out-of-range=Build part of version must be in the range [0, 65535]
+error.version-string-minor-out-of-range=Minor version must be in the range [0, 255]
+error.version-string-part-not-number=Failed to convert version component to int.
+error.cannot-walk-directory=Can not walk [{0}] - it is not a valid directory
+error.version-swap=Failed to update version information for {0}.
+
+
+
+message.result-dir=Result application bundle\: {0}
+message.icon-not-ico=The specified icon "{0}" is not an ICO file and will not be used.  The default icon will be used in it's place.
+message.multiple-launchers=Multiple launchers found in predefined app-image.  {0} will be used as primary launcher.
+message.potential.windows.defender.issue=Warning: Windows Defender may prevent jpackage from functioning. If there is an issue, it can be addressed by either disabling realtime monitoring, or adding an exclusion for the directory "{0}".
+message.tool-wrong-version=Detected [{0}] version {1} but version {2} is required.
+message.outputting-to-location=Generating EXE for installer to\: {0}
+message.output-location=Installer (.exe) saved to\: {0}
+message.tool-version=\  Detected [{0}] version [{1}]
+message.one-shortcut-required=At least one type of shortcut is required. Enabling menu shortcut.
+message.running-wsh-script=Running WSH script on application image [{0}]
+message.iscc-file-string=\  InnoSetup compiler set to {0}
+message.creating-association-with-null-extension=Creating association with null extension.
+message.wrong-tool-version=Detected [{0}] version {1} but version {2} is required.
+message.version-string-too-many-components=Version sting may have up to 3 components - major.minor.build .
+message.truncating.id=Truncating Application ID to 126 chars for Inno Setup.
+message.use-wix36-features=WiX 3.6 detected. Enabling advanced cleanup action.
+message.generated-product-guid=Generated product GUID\: {0}
+message.preparing-msi-config=Preparing MSI config\: {0}
+message.generating-msi=Generating MSI\: {0}
+message.light-file-string=WiX light tool set to {0}
+message.candle-file-string=WiX candle tool set to {0}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_ja.properties	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,132 @@
+#
+# Copyright (c) 2017, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+# particular file as subject to the "Classpath" exception as provided
+# by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+# or visit www.oracle.com if you need additional information or have any
+# questions.
+#
+#
+
+app.bundler.name=Windows Application Image
+app.bundler.description=A Directory based image of a windows Application with an optionally co-bundled JRE.  Used as a base for the Installer bundlers
+exe.bundler.name=EXE Installer
+exe.bundler.description=Microsoft Windows EXE Installer, via InnoIDE.
+msi.bundler.name=MSI Installer
+msi.bundler.description=Microsoft Windows MSI Installer, via WiX.
+
+param.raw-executable-url.name=Launcher URL
+param.raw-executable-url.description=Override the jpackage default launcher with a custom launcher.
+param.rebrand-executable.name=Rebrand Launcher
+param.rebrand-executable.description=Update the launcher with the application icon and update ownership information.
+param.icon-ico.name=.ico Icon
+param.icon-ico.description=Icon for the application, in ICO format.
+param.console-hint.name=Console Hint
+param.console-hint.description=Indicates if the bundler should use console launcher
+param.menu-group.name=Menu Group
+param.menu-group.description=The Start Menu group this application should be placed in
+param.menu-group.default=Unknown
+param.installer-name.name=Installer Name
+param.installer-name.description=The filename of the generated installer without the file type extension.  Default is <App Name>-<Version>.
+param.registry-name.name=Registry Name
+param.registry-name.description=The name of the application for registry references.  Default is the Application Name with only alphanumerics, dots, and dashes (no whitespace).
+param.installdir-chooser.name=Install Directory Chooser
+param.installdir-chooser.description=Adds a dialog to let the user choose a directory where the product will be installed.
+param.system-wide.name=System Wide
+param.system-wide.description=Should this application attempt to install itself system wide, or only for each user?  Null means use the system default.
+param.exe-bundler.name=Exe Installer Bundler
+param.exe-bundler.description=Exe Installer Bundler
+param.image-dir.name=Image Dir
+param.image-dir.description=Image Dir
+param.app-dir.name=App Dir
+param.app-dir.description=App Dir
+param.menu-shortcut-hint.name=Menu Hint
+param.menu-shortcut-hint.description=If the bundler can add the application to the system menu, should it?
+param.desktop-shortcut-hint.name=Shortcut Hint
+param.desktop-shortcut-hint.description=If the bundler can create desktop shortcuts, should it make one?
+param.upgrade-uuid.name=Upgrade UUID
+param.upgrade-uuid.description=The UUID associated with upgrades for this package.
+param.product-version.name=Product Version
+param.product-version.description=The version of the application as seen by Windows and MSI, of the form "1.2.3"
+param.iscc-path.name=InnoSetup iscc.exe location
+param.iscc-path.description=File path to iscc.exe from the InnoSetup tool.
+param.msi-bundler.name=MSI App Bundler
+param.msi-bundler.description=MSI App Bundler
+param.can-use-wix36.name=Can Use Wix
+param.can-use-wix36.description=Can Use Wix
+param.candle-path.name=WiX candle.exe location
+param.candle-path.description=File path to candle.exe from the WiX toolset.
+param.light-path.name=WiX light.exe location
+param.light-path.description=File path to light.exe from the WiX toolset.
+
+resource.application-icon=application icon
+resource.executable-properties-template=Template for creating executable properties file.
+resource.inno-setup-project-file=Inno Setup project file
+resource.setup-icon=setup dialog icon
+resource.post-install-script=script to run after application image is populated
+resource.wix-config-file=WiX config file
+
+error.parameters-null=Parameters map is null.
+error.parameters-null.advice=Pass in a non-null parameters map.
+error.no-windows-resources=This copy of the JDK does not support Windows.
+error.no-windows-resources.advice=Please use the Oracle JDK for Windows.
+error.cannot-find-launcher=Cannot find cfg file in predefined app image directory {0}.
+error.cannot-create-output-dir=Output directory {0} cannot be created.
+error.cannot-write-to-output-dir=Output directory {0} is not writable.
+error.iscc-not-found=Can not find Inno Setup Compiler (iscc.exe).
+error.iscc-not-found.advice=Download Inno Setup 5 or later from http\://www.jrsoftware.org and add it to the PATH.
+error.copyright-is-too-long=The copyright string is too long for InnoSetup.
+error.copyright-is-too-long.advice=Provide a copyright string shorter than 100 characters.
+error.too-many-content-types-for-file-association=More than one MIME types was specified for File Association number {0}.
+error.too-many-content-types-for-file-association.advice=Specify one and only one MIME type for each file association.
+error.no-wix-tools=Can not find WiX tools (light.exe, candle.exe).
+error.no-wix-tools.advice=Download WiX 3.0 or later from http\://wix.sf.net and add it to the PATH.
+error.version-string-wrong-format=Version string is not compatible with MSI rules [{0}].
+error.version-string-wrong-format.advice=Set the bundler argument "{0}" according to these rules: http\://msdn.microsoft.com/en-us/library/aa370859%28v\=VS.85%29.aspx
+error.version-string-major-out-of-range=Major version must be in the range [0, 255]
+error.version-string-build-out-of-range=Build part of version must be in the range [0, 65535]
+error.version-string-minor-out-of-range=Minor version must be in the range [0, 255]
+error.version-string-part-not-number=Failed to convert version component to int.
+error.cannot-walk-directory=Can not walk [{0}] - it is not a valid directory
+error.version-swap=Failed to update version information for {0}.
+
+
+
+message.result-dir=Result application bundle\: {0}
+message.icon-not-ico=The specified icon "{0}" is not an ICO file and will not be used.  The default icon will be used in it's place.
+message.multiple-launchers=Multiple launchers found in predefined app-image.  {0} will be used as primary launcher.
+message.potential.windows.defender.issue=Warning: Windows Defender may prevent jpackage from functioning. If there is an issue, it can be addressed by either disabling realtime monitoring, or adding an exclusion for the directory "{0}".
+message.tool-wrong-version=Detected [{0}] version {1} but version {2} is required.
+message.outputting-to-location=Generating EXE for installer to\: {0}
+message.output-location=Installer (.exe) saved to\: {0}
+message.tool-version=\  Detected [{0}] version [{1}]
+message.one-shortcut-required=At least one type of shortcut is required. Enabling menu shortcut.
+message.running-wsh-script=Running WSH script on application image [{0}]
+message.iscc-file-string=\  InnoSetup compiler set to {0}
+message.creating-association-with-null-extension=Creating association with null extension.
+message.wrong-tool-version=Detected [{0}] version {1} but version {2} is required.
+message.version-string-too-many-components=Version sting may have up to 3 components - major.minor.build .
+message.truncating.id=Truncating Application ID to 126 chars for Inno Setup.
+message.use-wix36-features=WiX 3.6 detected. Enabling advanced cleanup action.
+message.generated-product-guid=Generated product GUID\: {0}
+message.preparing-msi-config=Preparing MSI config\: {0}
+message.generating-msi=Generating MSI\: {0}
+message.light-file-string=WiX light tool set to {0}
+message.candle-file-string=WiX candle tool set to {0}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_zh_CN.properties	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,132 @@
+#
+# Copyright (c) 2017, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+# particular file as subject to the "Classpath" exception as provided
+# by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+# or visit www.oracle.com if you need additional information or have any
+# questions.
+#
+#
+
+app.bundler.name=Windows Application Image
+app.bundler.description=A Directory based image of a windows Application with an optionally co-bundled JRE.  Used as a base for the Installer bundlers
+exe.bundler.name=EXE Installer
+exe.bundler.description=Microsoft Windows EXE Installer, via InnoIDE.
+msi.bundler.name=MSI Installer
+msi.bundler.description=Microsoft Windows MSI Installer, via WiX.
+
+param.raw-executable-url.name=Launcher URL
+param.raw-executable-url.description=Override the jpackage default launcher with a custom launcher.
+param.rebrand-executable.name=Rebrand Launcher
+param.rebrand-executable.description=Update the launcher with the application icon and update ownership information.
+param.icon-ico.name=.ico Icon
+param.icon-ico.description=Icon for the application, in ICO format.
+param.console-hint.name=Console Hint
+param.console-hint.description=Indicates if the bundler should use console launcher
+param.menu-group.name=Menu Group
+param.menu-group.description=The Start Menu group this application should be placed in
+param.menu-group.default=Unknown
+param.installer-name.name=Installer Name
+param.installer-name.description=The filename of the generated installer without the file type extension.  Default is <App Name>-<Version>.
+param.registry-name.name=Registry Name
+param.registry-name.description=The name of the application for registry references.  Default is the Application Name with only alphanumerics, dots, and dashes (no whitespace).
+param.installdir-chooser.name=Install Directory Chooser
+param.installdir-chooser.description=Adds a dialog to let the user choose a directory where the product will be installed.
+param.system-wide.name=System Wide
+param.system-wide.description=Should this application attempt to install itself system wide, or only for each user?  Null means use the system default.
+param.exe-bundler.name=Exe Installer Bundler
+param.exe-bundler.description=Exe Installer Bundler
+param.image-dir.name=Image Dir
+param.image-dir.description=Image Dir
+param.app-dir.name=App Dir
+param.app-dir.description=App Dir
+param.menu-shortcut-hint.name=Menu Hint
+param.menu-shortcut-hint.description=If the bundler can add the application to the system menu, should it?
+param.desktop-shortcut-hint.name=Shortcut Hint
+param.desktop-shortcut-hint.description=If the bundler can create desktop shortcuts, should it make one?
+param.upgrade-uuid.name=Upgrade UUID
+param.upgrade-uuid.description=The UUID associated with upgrades for this package.
+param.product-version.name=Product Version
+param.product-version.description=The version of the application as seen by Windows and MSI, of the form "1.2.3"
+param.iscc-path.name=InnoSetup iscc.exe location
+param.iscc-path.description=File path to iscc.exe from the InnoSetup tool.
+param.msi-bundler.name=MSI App Bundler
+param.msi-bundler.description=MSI App Bundler
+param.can-use-wix36.name=Can Use Wix
+param.can-use-wix36.description=Can Use Wix
+param.candle-path.name=WiX candle.exe location
+param.candle-path.description=File path to candle.exe from the WiX toolset.
+param.light-path.name=WiX light.exe location
+param.light-path.description=File path to light.exe from the WiX toolset.
+
+resource.application-icon=application icon
+resource.executable-properties-template=Template for creating executable properties file.
+resource.inno-setup-project-file=Inno Setup project file
+resource.setup-icon=setup dialog icon
+resource.post-install-script=script to run after application image is populated
+resource.wix-config-file=WiX config file
+
+error.parameters-null=Parameters map is null.
+error.parameters-null.advice=Pass in a non-null parameters map.
+error.no-windows-resources=This copy of the JDK does not support Windows.
+error.no-windows-resources.advice=Please use the Oracle JDK for Windows.
+error.cannot-find-launcher=Cannot find cfg file in predefined app image directory {0}.
+error.cannot-create-output-dir=Output directory {0} cannot be created.
+error.cannot-write-to-output-dir=Output directory {0} is not writable.
+error.iscc-not-found=Can not find Inno Setup Compiler (iscc.exe).
+error.iscc-not-found.advice=Download Inno Setup 5 or later from http\://www.jrsoftware.org and add it to the PATH.
+error.copyright-is-too-long=The copyright string is too long for InnoSetup.
+error.copyright-is-too-long.advice=Provide a copyright string shorter than 100 characters.
+error.too-many-content-types-for-file-association=More than one MIME types was specified for File Association number {0}.
+error.too-many-content-types-for-file-association.advice=Specify one and only one MIME type for each file association.
+error.no-wix-tools=Can not find WiX tools (light.exe, candle.exe).
+error.no-wix-tools.advice=Download WiX 3.0 or later from http\://wix.sf.net and add it to the PATH.
+error.version-string-wrong-format=Version string is not compatible with MSI rules [{0}].
+error.version-string-wrong-format.advice=Set the bundler argument "{0}" according to these rules: http\://msdn.microsoft.com/en-us/library/aa370859%28v\=VS.85%29.aspx
+error.version-string-major-out-of-range=Major version must be in the range [0, 255]
+error.version-string-build-out-of-range=Build part of version must be in the range [0, 65535]
+error.version-string-minor-out-of-range=Minor version must be in the range [0, 255]
+error.version-string-part-not-number=Failed to convert version component to int.
+error.cannot-walk-directory=Can not walk [{0}] - it is not a valid directory
+error.version-swap=Failed to update version information for {0}.
+
+
+
+message.result-dir=Result application bundle\: {0}
+message.icon-not-ico=The specified icon "{0}" is not an ICO file and will not be used.  The default icon will be used in it's place.
+message.multiple-launchers=Multiple launchers found in predefined app-image.  {0} will be used as primary launcher.
+message.potential.windows.defender.issue=Warning: Windows Defender may prevent jpackage from functioning. If there is an issue, it can be addressed by either disabling realtime monitoring, or adding an exclusion for the directory "{0}".
+message.tool-wrong-version=Detected [{0}] version {1} but version {2} is required.
+message.outputting-to-location=Generating EXE for installer to\: {0}
+message.output-location=Installer (.exe) saved to\: {0}
+message.tool-version=\  Detected [{0}] version [{1}]
+message.one-shortcut-required=At least one type of shortcut is required. Enabling menu shortcut.
+message.running-wsh-script=Running WSH script on application image [{0}]
+message.iscc-file-string=\  InnoSetup compiler set to {0}
+message.creating-association-with-null-extension=Creating association with null extension.
+message.wrong-tool-version=Detected [{0}] version {1} but version {2} is required.
+message.version-string-too-many-components=Version sting may have up to 3 components - major.minor.build .
+message.truncating.id=Truncating Application ID to 126 chars for Inno Setup.
+message.use-wix36-features=WiX 3.6 detected. Enabling advanced cleanup action.
+message.generated-product-guid=Generated product GUID\: {0}
+message.preparing-msi-config=Preparing MSI config\: {0}
+message.generating-msi=Generating MSI\: {0}
+message.light-file-string=WiX light tool set to {0}
+message.candle-file-string=WiX candle tool set to {0}
+
Binary file src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/icon_inno_setup.bmp has changed
Binary file src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/javalogo_white_48.ico has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/template.iss	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,72 @@
+;This file will be executed next to the application bundle image
+;I.e. current directory will contain folder INSTALLER_NAME with application files
+[Setup]
+AppId=PRODUCT_APP_IDENTIFIER
+AppName=INSTALLER_NAME
+AppVersion=APPLICATION_VERSION
+AppVerName=INSTALLER_NAME APPLICATION_VERSION
+AppPublisher=APPLICATION_VENDOR
+AppComments=APPLICATION_COMMENTS
+AppCopyright=APPLICATION_COPYRIGHT
+DefaultDirName=APPLICATION_INSTALL_ROOT\INSTALLER_NAME
+DisableStartupPrompt=Yes
+DisableDirPage=DISABLE_DIR_PAGE
+DisableProgramGroupPage=Yes
+DisableReadyPage=Yes
+DisableFinishedPage=Yes
+DisableWelcomePage=Yes
+DefaultGroupName=APPLICATION_GROUP
+;Optional License
+LicenseFile=APPLICATION_LICENSE_FILE
+;WinXP or above
+MinVersion=0,5.1
+OutputBaseFilename=INSTALLER_FILE_NAME
+Compression=lzma
+SolidCompression=yes
+PrivilegesRequired=APPLICATION_INSTALL_PRIVILEGE
+SetupIconFile=INSTALLER_NAME\LAUNCHER_NAME.ico
+UninstallDisplayIcon={app}\LAUNCHER_NAME.ico
+UninstallDisplayName=INSTALLER_NAME
+WizardImageStretch=No
+WizardSmallImageFile=INSTALLER_NAME-setup-icon.bmp
+ArchitecturesInstallIn64BitMode=ARCHITECTURE_BIT_MODE
+FILE_ASSOCIATIONS
+
+[Languages]
+Name: "english"; MessagesFile: "compiler:Default.isl"
+
+[Files]
+Source: "INSTALLER_NAME\LAUNCHER_NAME.exe"; DestDir: "{app}"; Flags: ignoreversion
+Source: "INSTALLER_NAME\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
+
+[Icons]
+Name: "{group}\INSTALLER_NAME"; Filename: "{app}\LAUNCHER_NAME.exe"; IconFilename: "{app}\LAUNCHER_NAME.ico"; Check: APPLICATION_MENU_SHORTCUT()
+Name: "{commondesktop}\INSTALLER_NAME"; Filename: "{app}\LAUNCHER_NAME.exe";  IconFilename: "{app}\LAUNCHER_NAME.ico"; Check: APPLICATION_DESKTOP_SHORTCUT()
+SECONDARY_LAUNCHERS
+
+[Run]
+Filename: "{app}\RUN_FILENAME.exe"; Parameters: "-Xappcds:generatecache"; Check: APPLICATION_APP_CDS_INSTALL()
+Filename: "{app}\RUN_FILENAME.exe"; Description: "{cm:LaunchProgram,INSTALLER_NAME}"; Flags: nowait postinstall skipifsilent; Check: APPLICATION_NOT_SERVICE()
+Filename: "{app}\RUN_FILENAME.exe"; Parameters: "-install -svcName ""INSTALLER_NAME"" -svcDesc ""APPLICATION_DESCRIPTION"" -mainExe ""APPLICATION_LAUNCHER_FILENAME"" START_ON_INSTALL RUN_AT_STARTUP"; Check: APPLICATION_SERVICE()
+
+[UninstallRun]
+Filename: "{app}\RUN_FILENAME.exe "; Parameters: "-uninstall -svcName INSTALLER_NAME STOP_ON_UNINSTALL"; Check: APPLICATION_SERVICE()
+
+[Code]
+function returnTrue(): Boolean;
+begin
+  Result := True;
+end;
+
+function returnFalse(): Boolean;
+begin
+  Result := False;
+end;
+
+function InitializeSetup(): Boolean;
+begin
+// Possible future improvements:
+//   if version less or same => just launch app
+//   if upgrade => check if same app is running and wait for it to exit
+  Result := True;
+end;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/template.jre.iss	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,55 @@
+;This file will be executed next to the application bundle image
+;I.e. current directory will contain folder INSTALLER_NAME with application files
+[Setup]
+AppId=PRODUCT_APP_IDENTIFIER
+AppName=INSTALLER_NAME
+AppVersion=APPLICATION_VERSION
+AppVerName=INSTALLER_NAME APPLICATION_VERSION
+AppPublisher=APPLICATION_VENDOR
+AppComments=APPLICATION_COMMENTS
+AppCopyright=APPLICATION_COPYRIGHT
+DefaultDirName=APPLICATION_INSTALL_ROOT\INSTALLER_NAME
+DisableStartupPrompt=Yes
+DisableDirPage=DISABLE_DIR_PAGE
+DisableProgramGroupPage=Yes
+DisableReadyPage=Yes
+DisableFinishedPage=Yes
+DisableWelcomePage=Yes
+DefaultGroupName=APPLICATION_GROUP
+;Optional License
+LicenseFile=APPLICATION_LICENSE_FILE
+;WinXP or above
+MinVersion=0,5.1
+OutputBaseFilename=INSTALLER_FILE_NAME
+Compression=lzma
+SolidCompression=yes
+PrivilegesRequired=APPLICATION_INSTALL_PRIVILEGE
+SetupIconFile=
+UninstallDisplayIcon=
+UninstallDisplayName=INSTALLER_NAME
+WizardImageStretch=No
+WizardSmallImageFile=INSTALLER_NAME-setup-icon.bmp
+ArchitecturesInstallIn64BitMode=ARCHITECTURE_BIT_MODE
+FILE_ASSOCIATIONS
+
+[Languages]
+Name: "english"; MessagesFile: "compiler:Default.isl"
+
+[Files]
+Source: "APPLICATION_IMAGE\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
+
+[Code]
+function returnTrue(): Boolean;
+begin
+  Result := True;
+end;
+
+function returnFalse(): Boolean;
+begin
+  Result := False;
+end;
+
+function InitializeSetup(): Boolean;
+begin
+  Result := True;
+end;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/template.jre.wxs	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
+     xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">
+    <Product Id="PRODUCT_GUID" Name="APPLICATION_NAME"
+             Language="1033" Version="APPLICATION_VERSION"
+             Manufacturer="APPLICATION_VENDOR"
+             UpgradeCode="PRODUCT_UPGRADE_GUID">
+        <Package Description="APPLICATION_DESCRIPTION" Comments="None"
+                 InstallerVersion="200" Compressed="yes"
+                 InstallScope="INSTALL_SCOPE" Platform="PLATFORM"/>
+        <Media Id="1" Cabinet="simple.cab" EmbedCab="yes" />
+UPGRADE_BLOCK
+
+        <!-- We use RemoveFolderEx to ensure application folder is fully
+             removed on uninstall. Including files created outside of MSI
+             after application had been installed (e.g. on AU or user state).
+
+             Hovewer, RemoveFolderEx is only available in WiX 3.6,
+             we will comment it out if we running older WiX.
+
+             RemoveFolderEx requires that we "remember" the path for uninstall.
+             Read the path value and set the APPLICATIONFOLDER property with the value.
+        -->
+        <Property Id="APPLICATIONFOLDER">
+            <RegistrySearch Key="SOFTWARE\APPLICATION_VENDOR\APPLICATION_NAME"
+                            Root="REGISTRY_ROOT" Type="raw"
+                            Id="APPLICATIONFOLDER_REGSEARCH" Name="Path" />
+        </Property>
+        <DirectoryRef Id="APPLICATIONFOLDER">
+            <Component Id="CleanupMainApplicationFolder" Guid="*" Win64="WIN64">
+                <RegistryValue Root="REGISTRY_ROOT"
+                                   Key="SOFTWARE\APPLICATION_VENDOR\APPLICATION_NAME"
+                                   Name="Path" Type="string" Value="[APPLICATIONFOLDER]"
+                                   KeyPath="yes" />
+                <!-- We need to use APPLICATIONFOLDER variable here or RemoveFolderEx
+                     will not remove on "install". But only if WiX 3.6 is used. -->
+                WIX36_ONLY_START
+                  <util:RemoveFolderEx On="uninstall" Property="APPLICATIONFOLDER" />
+                WIX36_ONLY_END
+            </Component>
+        </DirectoryRef>
+        <?include bundle.wxi ?>
+UI_BLOCK
+    </Product>
+</Wix>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/template.wxs	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
+     xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">
+    <Product Id="PRODUCT_GUID" Name="APPLICATION_NAME"
+             Language="1033" Version="APPLICATION_VERSION"
+             Manufacturer="APPLICATION_VENDOR"
+             UpgradeCode="PRODUCT_UPGRADE_GUID">
+        <Package Description="APPLICATION_DESCRIPTION" Comments="None"
+                 InstallerVersion="200" Compressed="yes"
+                 InstallScope="INSTALL_SCOPE" Platform="PLATFORM"/>
+        <Media Id="1" Cabinet="simple.cab" EmbedCab="yes" />
+UPGRADE_BLOCK
+
+        <!-- We use RemoveFolderEx to ensure application folder is fully
+             removed on uninstall. Including files created outside of MSI
+             after application had been installed (e.g. on AU or user state).
+
+             Hovewer, RemoveFolderEx is only available in WiX 3.6,
+             we will comment it out if we running older WiX.
+
+             RemoveFolderEx requires that we "remember" the path for uninstall.
+             Read the path value and set the APPLICATIONFOLDER property with the value.
+        -->
+        <Property Id="APPLICATIONFOLDER">
+            <RegistrySearch Key="SOFTWARE\APPLICATION_VENDOR\APPLICATION_NAME"
+                            Root="REGISTRY_ROOT" Type="raw"
+                            Id="APPLICATIONFOLDER_REGSEARCH" Name="Path" />
+        </Property>
+        <DirectoryRef Id="APPLICATIONFOLDER">
+            <Component Id="CleanupMainApplicationFolder" Guid="*" Win64="WIN64">
+                <RegistryValue Root="REGISTRY_ROOT"
+                                   Key="SOFTWARE\APPLICATION_VENDOR\APPLICATION_NAME"
+                                   Name="Path" Type="string" Value="[APPLICATIONFOLDER]"
+                                   KeyPath="yes" />
+                <!-- We need to use APPLICATIONFOLDER variable here or RemoveFolderEx
+                     will not remove on "install". But only if WiX 3.6 is used. -->
+                WIX36_ONLY_START
+                  <util:RemoveFolderEx On="uninstall" Property="APPLICATIONFOLDER" />
+                WIX36_ONLY_END
+            </Component>
+        </DirectoryRef>
+        <?include bundle.wxi ?>
+UI_BLOCK
+        <Icon Id="DesktopIcon.exe" SourceFile="APPLICATION_ICON" />
+        <Icon Id="StartMenuIcon.exe" SourceFile="APPLICATION_ICON" />
+SECONDARY_LAUNCHER_ICONS
+    </Product>
+</Wix>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/classes/module-info.java.extra	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+provides jdk.jpackage.internal.Bundler with
+    jdk.jpackage.internal.WinAppBundler,
+    jdk.jpackage.internal.WinExeBundler,
+    jdk.jpackage.internal.WinMsiBundler;
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/jpackageapplauncher/WinLauncher.cpp	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2012, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include <Windows.h>
+#include <Shellapi.h>
+#include <locale.h>
+#include <tchar.h>
+#include <string>
+
+#define JPACKAGE_LIBRARY TEXT("applauncher.dll")
+
+typedef bool (*start_launcher)(int argc, TCHAR* argv[]);
+typedef void (*stop_launcher)();
+
+std::wstring GetTitle() {
+    std::wstring result;
+    wchar_t buffer[MAX_PATH];
+    GetModuleFileName(NULL, buffer, MAX_PATH - 1);
+    buffer[MAX_PATH - 1] = '\0';
+    result = buffer;
+    size_t slash = result.find_last_of('\\');
+
+    if (slash != std::wstring::npos)
+        result = result.substr(slash + 1, result.size() - slash - 1);
+
+    return result;
+}
+
+#ifdef LAUNCHERC
+int main(int argc0, char *argv0[]) {
+#else // LAUNCHERC
+int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
+                       LPTSTR lpCmdLine, int nCmdShow) {
+#endif // LAUNCHERC
+    int result = 1;
+    TCHAR **argv;
+    int argc;
+
+    // [RT-31061] otherwise UI can be left in back of other windows.
+    ::AllowSetForegroundWindow(ASFW_ANY);
+
+    ::setlocale(LC_ALL, "en_US.utf8");
+    argv = CommandLineToArgvW(GetCommandLine(), &argc);
+
+    HMODULE library = ::LoadLibrary(JPACKAGE_LIBRARY);
+
+    if (library == NULL) {
+        std::wstring title = GetTitle();
+        std::wstring description = std::wstring(JPACKAGE_LIBRARY)
+                + std::wstring(TEXT(" not found."));
+        MessageBox(NULL, description.data(),
+                title.data(), MB_ICONERROR | MB_OK);
+    }
+    else {
+        start_launcher start =
+                (start_launcher)GetProcAddress(library, "start_launcher");
+        stop_launcher stop =
+                (stop_launcher)GetProcAddress(library, "stop_launcher");
+
+        if (start != NULL && stop != NULL) {
+            if (start(argc, argv) == true) {
+                result = 0;
+                stop();
+            }
+        }
+
+        ::FreeLibrary(library);
+    }
+
+    if (argv != NULL) {
+        LocalFree(argv);
+    }
+
+    return result;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/libapplauncher/DllMain.cpp	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include <windows.h>
+
+extern "C" {
+
+    BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason,
+            LPVOID lpvReserved) {
+        return true;
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/libapplauncher/FileAttribute.h	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef FILEATTRIBUTE_H
+#define FILEATTRIBUTE_H
+
+enum FileAttribute {
+    faArchive = FILE_ATTRIBUTE_ARCHIVE,
+    faCompressed = FILE_ATTRIBUTE_COMPRESSED,
+    faDevice = FILE_ATTRIBUTE_DEVICE,
+    faDirectory = FILE_ATTRIBUTE_DIRECTORY,
+    faEncrypted = FILE_ATTRIBUTE_ENCRYPTED,
+    faHidden = FILE_ATTRIBUTE_HIDDEN,
+    //faIntegrityStream = FILE_ATTRIBUTE_INTEGRITY_STREAM,
+    faNormal = FILE_ATTRIBUTE_NORMAL,
+    faNotContentIndexed = FILE_ATTRIBUTE_NOT_CONTENT_INDEXED,
+    //faNoScrubData = FILE_ATTRIBUTE_NO_SCRUB_DATA,
+    faOffline = FILE_ATTRIBUTE_OFFLINE,
+    faSystem = FILE_ATTRIBUTE_SYSTEM,
+    faSymbolicLink = FILE_ATTRIBUTE_REPARSE_POINT,
+    faSparceFile = FILE_ATTRIBUTE_SPARSE_FILE,
+    faReadOnly = FILE_ATTRIBUTE_READONLY,
+    faTemporary = FILE_ATTRIBUTE_TEMPORARY,
+    faVirtual = FILE_ATTRIBUTE_VIRTUAL
+};
+
+#endif // FILEATTRIBUTE_H
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/libapplauncher/FilePath.cpp	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,473 @@
+/*
+ * Copyright (c) 2014, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "FilePath.h"
+
+#include <algorithm>
+#include <list>
+#include <ShellAPI.h>
+
+bool FilePath::FileExists(const TString FileName) {
+    bool result = false;
+    WIN32_FIND_DATA FindFileData;
+    TString fileName = FixPathForPlatform(FileName);
+    HANDLE handle = FindFirstFile(fileName.data(), &FindFileData);
+
+    if (handle != INVALID_HANDLE_VALUE) {
+        if (FILE_ATTRIBUTE_DIRECTORY & FindFileData.dwFileAttributes) {
+            result = true;
+        }
+        else {
+            result = true;
+        }
+
+        FindClose(handle);
+    }
+    return result;
+}
+
+bool FilePath::DirectoryExists(const TString DirectoryName) {
+    bool result = false;
+    WIN32_FIND_DATA FindFileData;
+    TString directoryName = FixPathForPlatform(DirectoryName);
+    HANDLE handle = FindFirstFile(directoryName.data(), &FindFileData);
+
+    if (handle != INVALID_HANDLE_VALUE) {
+        if (FILE_ATTRIBUTE_DIRECTORY & FindFileData.dwFileAttributes) {
+            result = true;
+        }
+
+        FindClose(handle);
+    }
+    return result;
+}
+
+std::string GetLastErrorAsString() {
+    // Get the error message, if any.
+    DWORD errorMessageID = ::GetLastError();
+
+    if (errorMessageID == 0) {
+        return "No error message has been recorded";
+    }
+
+    LPSTR messageBuffer = NULL;
+    size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER
+            | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+            NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL,
+            SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL);
+
+    std::string message(messageBuffer, size);
+
+    // Free the buffer.
+    LocalFree(messageBuffer);
+
+    return message;
+}
+
+bool FilePath::DeleteFile(const TString FileName) {
+    bool result = false;
+
+    if (FileExists(FileName) == true) {
+        TString lFileName = FixPathForPlatform(FileName);
+        FileAttributes attributes(lFileName);
+
+        if (attributes.Contains(faReadOnly) == true) {
+            attributes.Remove(faReadOnly);
+        }
+
+        result = ::DeleteFile(lFileName.data()) == TRUE;
+    }
+
+    return result;
+}
+
+bool FilePath::DeleteDirectory(const TString DirectoryName) {
+    bool result = false;
+
+    if (DirectoryExists(DirectoryName) == true) {
+        SHFILEOPSTRUCTW fos = {0};
+        TString directoryName = FixPathForPlatform(DirectoryName);
+        DynamicBuffer<TCHAR> lDirectoryName(directoryName.size() + 2);
+        if (lDirectoryName.GetData() == NULL) {
+            return false;
+        }
+        memcpy(lDirectoryName.GetData(), directoryName.data(), (directoryName.size() + 2) * sizeof(TCHAR));
+        lDirectoryName[directoryName.size() + 1] = NULL;
+        // Double null terminate for SHFileOperation.
+
+        // Delete the folder and everything inside.
+        fos.wFunc = FO_DELETE;
+        fos.pFrom = lDirectoryName.GetData();
+        fos.fFlags = FOF_NO_UI;
+        result = SHFileOperation(&fos) == 0;
+    }
+
+    return result;
+}
+
+TString FilePath::IncludeTrailingSeparator(const TString value) {
+    TString result = value;
+
+    if (value.size() > 0) {
+        TString::iterator i = result.end();
+        i--;
+
+        if (*i != TRAILING_PATHSEPARATOR) {
+            result += TRAILING_PATHSEPARATOR;
+        }
+    }
+
+    return result;
+}
+
+TString FilePath::IncludeTrailingSeparator(const char* value) {
+    TString lvalue = PlatformString(value).toString();
+    return IncludeTrailingSeparator(lvalue);
+}
+
+TString FilePath::IncludeTrailingSeparator(const wchar_t* value) {
+    TString lvalue = PlatformString(value).toString();
+    return IncludeTrailingSeparator(lvalue);
+}
+
+TString FilePath::ExtractFilePath(TString Path) {
+    TString result;
+    size_t slash = Path.find_last_of(TRAILING_PATHSEPARATOR);
+    if (slash != TString::npos)
+        result = Path.substr(0, slash);
+    return result;
+}
+
+TString FilePath::ExtractFileExt(TString Path) {
+    TString result;
+    size_t dot = Path.find_last_of('.');
+
+    if (dot != TString::npos) {
+        result  = Path.substr(dot, Path.size() - dot);
+    }
+
+    return result;
+}
+
+TString FilePath::ExtractFileName(TString Path) {
+    TString result;
+
+    size_t slash = Path.find_last_of(TRAILING_PATHSEPARATOR);
+    if (slash != TString::npos)
+        result = Path.substr(slash + 1, Path.size() - slash - 1);
+
+    return result;
+}
+
+TString FilePath::ChangeFileExt(TString Path, TString Extension) {
+    TString result;
+    size_t dot = Path.find_last_of('.');
+
+    if (dot != TString::npos) {
+        result = Path.substr(0, dot) + Extension;
+    }
+
+    if (result.empty() == true) {
+        result = Path;
+    }
+
+    return result;
+}
+
+TString FilePath::FixPathForPlatform(TString Path) {
+    TString result = Path;
+    std::replace(result.begin(), result.end(),
+            BAD_TRAILING_PATHSEPARATOR, TRAILING_PATHSEPARATOR);
+    // The maximum path that does not require long path prefix. On Windows the
+    // maximum path is 260 minus 1 (NUL) but for directories it is 260 minus
+    // 12 minus 1 (to allow for the creation of a 8.3 file in the directory).
+    const int maxPath = 247;
+    if (result.length() > maxPath &&
+        result.find(_T("\\\\?\\")) == TString::npos &&
+        result.find(_T("\\\\?\\UNC")) == TString::npos) {
+        const TString prefix(_T("\\\\"));
+        if (!result.compare(0, prefix.size(), prefix)) {
+            // UNC path, converting to UNC path in long notation
+            result = _T("\\\\?\\UNC") + result.substr(1, result.length());
+        } else {
+            // converting to non-UNC path in long notation
+            result = _T("\\\\?\\") + result;
+        }
+    }
+    return result;
+}
+
+TString FilePath::FixPathSeparatorForPlatform(TString Path) {
+    TString result = Path;
+    std::replace(result.begin(), result.end(),
+            BAD_PATH_SEPARATOR, PATH_SEPARATOR);
+    return result;
+}
+
+TString FilePath::PathSeparator() {
+    TString result;
+    result = PATH_SEPARATOR;
+    return result;
+}
+
+bool FilePath::CreateDirectory(TString Path, bool ownerOnly) {
+    bool result = false;
+
+    std::list<TString> paths;
+    TString lpath = Path;
+
+    while (lpath.empty() == false && DirectoryExists(lpath) == false) {
+        paths.push_front(lpath);
+        lpath = ExtractFilePath(lpath);
+    }
+
+    for (std::list<TString>::iterator iterator = paths.begin();
+            iterator != paths.end(); iterator++) {
+        lpath = *iterator;
+
+        if (_wmkdir(lpath.data()) == 0) {
+            result = true;
+        } else {
+            result = false;
+            break;
+        }
+    }
+
+    return result;
+}
+
+void FilePath::ChangePermissions(TString FileName, bool ownerOnly) {
+}
+
+#include <algorithm>
+
+FileAttributes::FileAttributes(const TString FileName, bool FollowLink) {
+    FFileName = FileName;
+    FFollowLink = FollowLink;
+    ReadAttributes();
+}
+
+bool FileAttributes::WriteAttributes() {
+    bool result = false;
+
+    DWORD attributes = 0;
+
+    for (std::vector<FileAttribute>::const_iterator iterator =
+            FAttributes.begin();
+        iterator != FAttributes.end(); iterator++) {
+        switch (*iterator) {
+            case faArchive: {
+                attributes = attributes & FILE_ATTRIBUTE_ARCHIVE;
+                break;
+            }
+            case faCompressed: {
+                attributes = attributes & FILE_ATTRIBUTE_COMPRESSED;
+                break;
+            }
+            case faDevice: {
+                attributes = attributes & FILE_ATTRIBUTE_DEVICE;
+                break;
+            }
+            case faDirectory: {
+                attributes = attributes & FILE_ATTRIBUTE_DIRECTORY;
+                break;
+            }
+            case faEncrypted: {
+                attributes = attributes & FILE_ATTRIBUTE_ENCRYPTED;
+                break;
+            }
+            case faHidden: {
+                attributes = attributes & FILE_ATTRIBUTE_HIDDEN;
+                break;
+            }
+            case faNormal: {
+                attributes = attributes & FILE_ATTRIBUTE_NORMAL;
+                break;
+            }
+            case faNotContentIndexed: {
+                attributes = attributes & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED;
+                break;
+            }
+            case faOffline: {
+                attributes = attributes & FILE_ATTRIBUTE_OFFLINE;
+                break;
+            }
+            case faSystem: {
+                attributes = attributes & FILE_ATTRIBUTE_SYSTEM;
+                break;
+            }
+            case faSymbolicLink: {
+                attributes = attributes & FILE_ATTRIBUTE_REPARSE_POINT;
+                break;
+            }
+            case faSparceFile: {
+                attributes = attributes & FILE_ATTRIBUTE_SPARSE_FILE;
+                break;
+            }
+            case faReadOnly: {
+                attributes = attributes & FILE_ATTRIBUTE_READONLY;
+                break;
+            }
+            case faTemporary: {
+                attributes = attributes & FILE_ATTRIBUTE_TEMPORARY;
+                break;
+            }
+            case faVirtual: {
+                attributes = attributes & FILE_ATTRIBUTE_VIRTUAL;
+                break;
+            }
+        }
+    }
+
+    if (::SetFileAttributes(FFileName.data(), attributes) != 0) {
+        result = true;
+    }
+
+    return result;
+}
+
+#define S_ISRUSR(m)    (((m) & S_IRWXU) == S_IRUSR)
+#define S_ISWUSR(m)    (((m) & S_IRWXU) == S_IWUSR)
+#define S_ISXUSR(m)    (((m) & S_IRWXU) == S_IXUSR)
+
+#define S_ISRGRP(m)    (((m) & S_IRWXG) == S_IRGRP)
+#define S_ISWGRP(m)    (((m) & S_IRWXG) == S_IWGRP)
+#define S_ISXGRP(m)    (((m) & S_IRWXG) == S_IXGRP)
+
+#define S_ISROTH(m)    (((m) & S_IRWXO) == S_IROTH)
+#define S_ISWOTH(m)    (((m) & S_IRWXO) == S_IWOTH)
+#define S_ISXOTH(m)    (((m) & S_IRWXO) == S_IXOTH)
+
+bool FileAttributes::ReadAttributes() {
+    bool result = false;
+
+    DWORD attributes = ::GetFileAttributes(FFileName.data());
+
+    if (attributes != INVALID_FILE_ATTRIBUTES) {
+        result = true;
+
+        if (attributes | FILE_ATTRIBUTE_ARCHIVE) {
+            FAttributes.push_back(faArchive);
+        }
+        if (attributes | FILE_ATTRIBUTE_COMPRESSED) {
+            FAttributes.push_back(faCompressed);
+        }
+        if (attributes | FILE_ATTRIBUTE_DEVICE) {
+            FAttributes.push_back(faDevice);
+        }
+        if (attributes | FILE_ATTRIBUTE_DIRECTORY) {
+            FAttributes.push_back(faDirectory);
+        }
+        if (attributes | FILE_ATTRIBUTE_ENCRYPTED) {
+            FAttributes.push_back(faEncrypted);
+        }
+        if (attributes | FILE_ATTRIBUTE_HIDDEN) {
+            FAttributes.push_back(faHidden);
+        }
+        // if (attributes | FILE_ATTRIBUTE_INTEGRITY_STREAM) {
+        //     FAttributes.push_back(faIntegrityStream);
+        // }
+        if (attributes | FILE_ATTRIBUTE_NORMAL) {
+            FAttributes.push_back(faNormal);
+        }
+        if (attributes | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED) {
+            FAttributes.push_back(faNotContentIndexed);
+        }
+        // if (attributes | FILE_ATTRIBUTE_NO_SCRUB_DATA) {
+        //     FAttributes.push_back(faNoScrubData);
+        // }
+        if (attributes | FILE_ATTRIBUTE_SYSTEM) {
+            FAttributes.push_back(faSystem);
+        }
+        if (attributes | FILE_ATTRIBUTE_OFFLINE) {
+            FAttributes.push_back(faOffline);
+        }
+        if (attributes | FILE_ATTRIBUTE_REPARSE_POINT) {
+            FAttributes.push_back(faSymbolicLink);
+        }
+        if (attributes | FILE_ATTRIBUTE_SPARSE_FILE) {
+            FAttributes.push_back(faSparceFile);
+        }
+        if (attributes | FILE_ATTRIBUTE_READONLY ) {
+            FAttributes.push_back(faReadOnly);
+        }
+        if (attributes | FILE_ATTRIBUTE_TEMPORARY) {
+            FAttributes.push_back(faTemporary);
+        }
+        if (attributes | FILE_ATTRIBUTE_VIRTUAL) {
+            FAttributes.push_back(faVirtual);
+        }
+    }
+
+    return result;
+}
+
+bool FileAttributes::Valid(const FileAttribute Value) {
+    bool result = false;
+
+    switch (Value) {
+        case faHidden:
+        case faReadOnly: {
+            result = true;
+            break;
+        }
+        default:
+            break;
+    }
+
+    return result;
+}
+
+void FileAttributes::Append(FileAttribute Value) {
+    if (Valid(Value) == true) {
+        FAttributes.push_back(Value);
+        WriteAttributes();
+    }
+}
+
+bool FileAttributes::Contains(FileAttribute Value) {
+    bool result = false;
+
+    std::vector<FileAttribute>::const_iterator iterator =
+            std::find(FAttributes.begin(), FAttributes.end(), Value);
+
+    if (iterator != FAttributes.end()) {
+        result = true;
+    }
+
+    return result;
+}
+
+void FileAttributes::Remove(FileAttribute Value) {
+    if (Valid(Value) == true) {
+        std::vector<FileAttribute>::iterator iterator =
+            std::find(FAttributes.begin(), FAttributes.end(), Value);
+
+        if (iterator != FAttributes.end()) {
+            FAttributes.erase(iterator);
+            WriteAttributes();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/libapplauncher/PlatformDefs.h	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef PLATFORM_DEFS_H
+#define PLATFORM_DEFS_H
+
+// Define Windows compatibility requirements XP or later
+#define WINVER 0x0600
+#define _WIN32_WINNT 0x0600
+
+#include <Windows.h>
+#include <tchar.h>
+#include <shlobj.h>
+#include <direct.h>
+#include <process.h>
+#include <malloc.h>
+#include <string>
+
+using namespace std;
+
+#ifndef WINDOWS
+#define WINDOWS
+#endif
+
+typedef std::wstring TString;
+#define StringLength wcslen
+
+#define TRAILING_PATHSEPARATOR '\\'
+#define BAD_TRAILING_PATHSEPARATOR '/'
+#define PATH_SEPARATOR ';'
+#define BAD_PATH_SEPARATOR ':'
+
+typedef ULONGLONG TPlatformNumber;
+typedef DWORD TProcessID;
+
+typedef void* Module;
+typedef void* Procedure;
+
+#endif // PLATFORM_DEFS_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/libapplauncher/WindowsPlatform.cpp	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,765 @@
+/*
+ * Copyright (c) 2014, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "Platform.h"
+
+#include "JavaVirtualMachine.h"
+#include "WindowsPlatform.h"
+#include "Package.h"
+#include "Helpers.h"
+#include "PlatformString.h"
+#include "Macros.h"
+
+#include <map>
+#include <vector>
+#include <regex>
+#include <fstream>
+#include <locale>
+#include <codecvt>
+
+using namespace std;
+
+#define WINDOWS_JPACKAGE_TMP_DIR \
+        L"\\AppData\\Local\\Java\\JPackage\\tmp"
+
+class Registry {
+private:
+    HKEY FKey;
+    HKEY FOpenKey;
+    bool FOpen;
+
+public:
+
+    Registry(HKEY Key) {
+        FOpen = false;
+        FKey = Key;
+    }
+
+    ~Registry() {
+        Close();
+    }
+
+    void Close() {
+        if (FOpen == true) {
+            RegCloseKey(FOpenKey);
+        }
+    }
+
+    bool Open(TString SubKey) {
+        bool result = false;
+        Close();
+
+        if (RegOpenKeyEx(FKey, SubKey.data(), 0, KEY_READ, &FOpenKey) ==
+                ERROR_SUCCESS) {
+            result = true;
+        }
+
+        return result;
+    }
+
+    std::list<TString> GetKeys() {
+        std::list<TString> result;
+        DWORD count;
+
+        if (RegQueryInfoKey(FOpenKey, NULL, NULL, NULL, NULL, NULL, NULL,
+                &count, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) {
+
+            DWORD length = 255;
+            DynamicBuffer<TCHAR> buffer(length);
+            if (buffer.GetData() == NULL) {
+                return result;
+            }
+
+            for (unsigned int index = 0; index < count; index++) {
+                buffer.Zero();
+                DWORD status = RegEnumValue(FOpenKey, index, buffer.GetData(),
+                        &length, NULL, NULL, NULL, NULL);
+
+                while (status == ERROR_MORE_DATA) {
+                    length = length * 2;
+                    if (!buffer.Resize(length)) {
+                        return result;
+                    }
+                    status = RegEnumValue(FOpenKey, index, buffer.GetData(),
+                            &length, NULL, NULL, NULL, NULL);
+                }
+
+                if (status == ERROR_SUCCESS) {
+                    TString value = buffer.GetData();
+                    result.push_back(value);
+                }
+            }
+        }
+
+        return result;
+    }
+
+    TString ReadString(TString Name) {
+        TString result;
+        DWORD length;
+        DWORD dwRet;
+        DynamicBuffer<wchar_t> buffer(0);
+        length = 0;
+
+        dwRet = RegQueryValueEx(FOpenKey, Name.data(), NULL, NULL, NULL,
+                &length);
+        if (dwRet == ERROR_MORE_DATA || dwRet == 0) {
+            if (!buffer.Resize(length + 1)) {
+                return result;
+            }
+            dwRet = RegQueryValueEx(FOpenKey, Name.data(), NULL, NULL,
+                    (LPBYTE) buffer.GetData(), &length);
+            result = buffer.GetData();
+        }
+
+        return result;
+    }
+};
+
+WindowsPlatform::WindowsPlatform(void) : Platform() {
+    FMainThread = ::GetCurrentThreadId();
+}
+
+WindowsPlatform::~WindowsPlatform(void) {
+}
+
+TString WindowsPlatform::GetPackageAppDirectory() {
+    return FilePath::IncludeTrailingSeparator(
+            GetPackageRootDirectory()) + _T("app");
+}
+
+TString WindowsPlatform::GetPackageLauncherDirectory() {
+    return GetPackageRootDirectory();
+}
+
+TString WindowsPlatform::GetPackageRuntimeBinDirectory() {
+    return FilePath::IncludeTrailingSeparator(GetPackageRootDirectory()) + _T("runtime\\bin");
+}
+
+TCHAR* WindowsPlatform::ConvertStringToFileSystemString(TCHAR* Source,
+        bool &release) {
+    // Not Implemented.
+    return NULL;
+}
+
+TCHAR* WindowsPlatform::ConvertFileSystemStringToString(TCHAR* Source,
+        bool &release) {
+    // Not Implemented.
+    return NULL;
+}
+
+void WindowsPlatform::SetCurrentDirectory(TString Value) {
+    _wchdir(Value.data());
+}
+
+TString WindowsPlatform::GetPackageRootDirectory() {
+    TString filename = GetModuleFileName();
+    return FilePath::ExtractFilePath(filename);
+}
+
+TString WindowsPlatform::GetAppDataDirectory() {
+    TString result;
+    TCHAR path[MAX_PATH];
+
+    if (SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, 0, path) == S_OK) {
+        result = path;
+    }
+
+    return result;
+}
+
+TString WindowsPlatform::GetAppName() {
+    TString result = GetModuleFileName();
+    result = FilePath::ExtractFileName(result);
+    result = FilePath::ChangeFileExt(result, _T(""));
+    return result;
+}
+
+void WindowsPlatform::ShowMessage(TString title, TString description) {
+    MessageBox(NULL, description.data(),
+            !title.empty() ? title.data() : description.data(),
+            MB_ICONERROR | MB_OK);
+}
+
+void WindowsPlatform::ShowMessage(TString description) {
+    TString appname = GetModuleFileName();
+    appname = FilePath::ExtractFileName(appname);
+    MessageBox(NULL, description.data(), appname.data(), MB_ICONERROR | MB_OK);
+}
+
+MessageResponse WindowsPlatform::ShowResponseMessage(TString title,
+        TString description) {
+    MessageResponse result = mrCancel;
+
+    if (::MessageBox(NULL, description.data(), title.data(), MB_OKCANCEL) ==
+            IDOK) {
+        result = mrOK;
+    }
+
+    return result;
+}
+
+TString WindowsPlatform::GetBundledJVMLibraryFileName(TString RuntimePath) {
+    TString result = FilePath::IncludeTrailingSeparator(RuntimePath) +
+            _T("jre\\bin\\jli.dll");
+
+    if (FilePath::FileExists(result) == false) {
+        result = FilePath::IncludeTrailingSeparator(RuntimePath) +
+                _T("bin\\jli.dll");
+    }
+
+    return result;
+}
+
+ISectionalPropertyContainer* WindowsPlatform::GetConfigFile(TString FileName) {
+    IniFile *result = new IniFile();
+    if (result == NULL) {
+        return NULL;
+    }
+
+    if (result->LoadFromFile(FileName) == false) {
+        // New property file format was not found,
+        // attempt to load old property file format.
+        Helpers::LoadOldConfigFile(FileName, result);
+    }
+
+    return result;
+}
+
+TString WindowsPlatform::GetModuleFileName() {
+    TString result;
+    DynamicBuffer<wchar_t> buffer(MAX_PATH);
+    if (buffer.GetData() == NULL) {
+        return result;
+    }
+
+    ::GetModuleFileName(NULL, buffer.GetData(),
+            static_cast<DWORD> (buffer.GetSize()));
+
+    while (ERROR_INSUFFICIENT_BUFFER == GetLastError()) {
+        if (!buffer.Resize(buffer.GetSize() * 2)) {
+            return result;
+        }
+        ::GetModuleFileName(NULL, buffer.GetData(),
+                static_cast<DWORD> (buffer.GetSize()));
+    }
+
+    result = buffer.GetData();
+    return result;
+}
+
+Module WindowsPlatform::LoadLibrary(TString FileName) {
+    return ::LoadLibrary(FileName.data());
+}
+
+void WindowsPlatform::FreeLibrary(Module AModule) {
+    ::FreeLibrary((HMODULE) AModule);
+}
+
+Procedure WindowsPlatform::GetProcAddress(Module AModule,
+        std::string MethodName) {
+    return ::GetProcAddress((HMODULE) AModule, MethodName.c_str());
+}
+
+bool WindowsPlatform::IsMainThread() {
+    bool result = (FMainThread == ::GetCurrentThreadId());
+    return result;
+}
+
+TString WindowsPlatform::GetTempDirectory() {
+    TString result;
+    PWSTR userDir = 0;
+
+    if (SUCCEEDED(SHGetKnownFolderPath(
+            FOLDERID_Profile,
+            0,
+            NULL,
+            &userDir))) {
+        result = userDir;
+        result += WINDOWS_JPACKAGE_TMP_DIR;
+        CoTaskMemFree(userDir);
+    }
+
+    return result;
+}
+
+static BOOL CALLBACK enumWindows(HWND winHandle, LPARAM lParam) {
+    DWORD pid = (DWORD) lParam, wPid = 0;
+    GetWindowThreadProcessId(winHandle, &wPid);
+    if (pid == wPid) {
+        SetForegroundWindow(winHandle);
+        return FALSE;
+    }
+    return TRUE;
+}
+
+TPlatformNumber WindowsPlatform::GetMemorySize() {
+    SYSTEM_INFO si;
+    GetSystemInfo(&si);
+    size_t result = (size_t) si.lpMaximumApplicationAddress;
+    result = result / 1048576; // Convert from bytes to megabytes.
+    return result;
+}
+
+std::vector<TString> FilterList(std::vector<TString> &Items,
+        std::wregex Pattern) {
+    std::vector<TString> result;
+
+    for (std::vector<TString>::iterator it = Items.begin();
+            it != Items.end(); ++it) {
+        TString item = *it;
+        std::wsmatch match;
+
+        if (std::regex_search(item, match, Pattern)) {
+            result.push_back(item);
+        }
+    }
+    return result;
+}
+
+Process* WindowsPlatform::CreateProcess() {
+    return new WindowsProcess();
+}
+
+void WindowsPlatform::InitStreamLocale(wios *stream) {
+    const std::locale empty_locale = std::locale::empty();
+    const std::locale utf8_locale =
+                std::locale(empty_locale, new std::codecvt_utf8<wchar_t>());
+    stream->imbue(utf8_locale);
+}
+
+void WindowsPlatform::addPlatformDependencies(JavaLibrary *pJavaLibrary) {
+    if (pJavaLibrary == NULL) {
+        return;
+    }
+
+    if (FilePath::FileExists(_T("msvcr100.dll")) == true) {
+        pJavaLibrary->AddDependency(_T("msvcr100.dll"));
+    }
+
+    TString runtimeBin = GetPackageRuntimeBinDirectory();
+    SetDllDirectory(runtimeBin.c_str());
+}
+
+void Platform::CopyString(char *Destination,
+        size_t NumberOfElements, const char *Source) {
+    strcpy_s(Destination, NumberOfElements, Source);
+
+    if (NumberOfElements > 0) {
+        Destination[NumberOfElements - 1] = '\0';
+    }
+}
+
+void Platform::CopyString(wchar_t *Destination,
+        size_t NumberOfElements, const wchar_t *Source) {
+    wcscpy_s(Destination, NumberOfElements, Source);
+
+    if (NumberOfElements > 0) {
+        Destination[NumberOfElements - 1] = '\0';
+    }
+}
+
+// Owner must free the return value.
+MultibyteString Platform::WideStringToMultibyteString(
+        const wchar_t* value) {
+    MultibyteString result;
+    size_t count = 0;
+
+    if (value == NULL) {
+        return result;
+    }
+
+    count = WideCharToMultiByte(CP_UTF8, 0, value, -1, NULL, 0, NULL, NULL);
+
+    if (count > 0) {
+        result.data = new char[count + 1];
+        result.length = WideCharToMultiByte(CP_UTF8, 0, value, -1,
+                result.data, (int)count, NULL, NULL);
+    }
+
+    return result;
+}
+
+// Owner must free the return value.
+WideString Platform::MultibyteStringToWideString(const char* value) {
+    WideString result;
+    size_t count = 0;
+
+    if (value == NULL) {
+        return result;
+    }
+
+    mbstowcs_s(&count, NULL, 0, value, _TRUNCATE);
+
+    if (count > 0) {
+        result.data = new wchar_t[count + 1];
+        mbstowcs_s(&result.length, result.data, count, value, count);
+    }
+
+    return result;
+}
+
+FileHandle::FileHandle(std::wstring FileName) {
+    FHandle = ::CreateFile(FileName.data(), GENERIC_READ, FILE_SHARE_READ,
+            NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
+}
+
+FileHandle::~FileHandle() {
+    if (IsValid() == true) {
+        ::CloseHandle(FHandle);
+    }
+}
+
+bool FileHandle::IsValid() {
+    return FHandle != INVALID_HANDLE_VALUE;
+}
+
+HANDLE FileHandle::GetHandle() {
+    return FHandle;
+}
+
+FileMappingHandle::FileMappingHandle(HANDLE FileHandle) {
+    FHandle = ::CreateFileMapping(FileHandle, NULL, PAGE_READONLY, 0, 0, NULL);
+}
+
+bool FileMappingHandle::IsValid() {
+    return FHandle != NULL;
+}
+
+FileMappingHandle::~FileMappingHandle() {
+    if (IsValid() == true) {
+        ::CloseHandle(FHandle);
+    }
+}
+
+HANDLE FileMappingHandle::GetHandle() {
+    return FHandle;
+}
+
+FileData::FileData(HANDLE Handle) {
+    FBaseAddress = ::MapViewOfFile(Handle, FILE_MAP_READ, 0, 0, 0);
+}
+
+FileData::~FileData() {
+    if (IsValid() == true) {
+        ::UnmapViewOfFile(FBaseAddress);
+    }
+}
+
+bool FileData::IsValid() {
+    return FBaseAddress != NULL;
+}
+
+LPVOID FileData::GetBaseAddress() {
+    return FBaseAddress;
+}
+
+WindowsLibrary::WindowsLibrary(std::wstring FileName) {
+    FFileName = FileName;
+}
+
+std::vector<TString> WindowsLibrary::GetImports() {
+    std::vector<TString> result;
+    FileHandle library(FFileName);
+
+    if (library.IsValid() == true) {
+        FileMappingHandle mapping(library.GetHandle());
+
+        if (mapping.IsValid() == true) {
+            FileData fileData(mapping.GetHandle());
+
+            if (fileData.IsValid() == true) {
+                PIMAGE_DOS_HEADER dosHeader =
+                        (PIMAGE_DOS_HEADER) fileData.GetBaseAddress();
+                PIMAGE_FILE_HEADER pImgFileHdr =
+                        (PIMAGE_FILE_HEADER) fileData.GetBaseAddress();
+                if (dosHeader->e_magic == IMAGE_DOS_SIGNATURE) {
+                    result = DumpPEFile(dosHeader);
+                }
+            }
+        }
+    }
+
+    return result;
+}
+
+// Given an RVA, look up the section header that encloses it and return a
+// pointer to its IMAGE_SECTION_HEADER
+
+PIMAGE_SECTION_HEADER WindowsLibrary::GetEnclosingSectionHeader(DWORD rva,
+        PIMAGE_NT_HEADERS pNTHeader) {
+    PIMAGE_SECTION_HEADER result = 0;
+    PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(pNTHeader);
+
+    for (unsigned index = 0; index < pNTHeader->FileHeader.NumberOfSections;
+            index++, section++) {
+        // Is the RVA is within this section?
+        if ((rva >= section->VirtualAddress) &&
+                (rva < (section->VirtualAddress + section->Misc.VirtualSize))) {
+            result = section;
+        }
+    }
+
+    return result;
+}
+
+LPVOID WindowsLibrary::GetPtrFromRVA(DWORD rva, PIMAGE_NT_HEADERS pNTHeader,
+        DWORD imageBase) {
+    LPVOID result = 0;
+    PIMAGE_SECTION_HEADER pSectionHdr = GetEnclosingSectionHeader(rva,
+            pNTHeader);
+
+    if (pSectionHdr != NULL) {
+        INT delta = (INT) (
+                pSectionHdr->VirtualAddress - pSectionHdr->PointerToRawData);
+        DWORD_PTR dwp = (DWORD_PTR) (imageBase + rva - delta);
+        result = reinterpret_cast<LPVOID> (dwp); // VS2017 - FIXME
+    }
+
+    return result;
+}
+
+std::vector<TString> WindowsLibrary::GetImportsSection(DWORD base,
+        PIMAGE_NT_HEADERS pNTHeader) {
+    std::vector<TString> result;
+
+    // Look up where the imports section is located. Normally in
+    // the .idata section,
+    // but not necessarily so. Therefore, grab the RVA from the data dir.
+    DWORD importsStartRVA = pNTHeader->OptionalHeader.DataDirectory[
+            IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
+
+    if (importsStartRVA != NULL) {
+        // Get the IMAGE_SECTION_HEADER that contains the imports. This is
+        // usually the .idata section, but doesn't have to be.
+        PIMAGE_SECTION_HEADER pSection =
+                GetEnclosingSectionHeader(importsStartRVA, pNTHeader);
+
+        if (pSection != NULL) {
+            PIMAGE_IMPORT_DESCRIPTOR importDesc =
+                    (PIMAGE_IMPORT_DESCRIPTOR) GetPtrFromRVA(
+                    importsStartRVA, pNTHeader, base);
+
+            if (importDesc != NULL) {
+                while (true) {
+                    // See if we've reached an empty IMAGE_IMPORT_DESCRIPTOR
+                    if ((importDesc->TimeDateStamp == 0) &&
+                            (importDesc->Name == 0)) {
+                        break;
+                    }
+
+                    std::string filename = (char*) GetPtrFromRVA(
+                            importDesc->Name, pNTHeader, base);
+                    result.push_back(PlatformString(filename));
+                    importDesc++; // advance to next IMAGE_IMPORT_DESCRIPTOR
+                }
+            }
+        }
+    }
+
+    return result;
+}
+
+std::vector<TString> WindowsLibrary::DumpPEFile(PIMAGE_DOS_HEADER dosHeader) {
+    std::vector<TString> result;
+    // all of this is VS2017 - FIXME
+    DWORD_PTR dwDosHeaders = reinterpret_cast<DWORD_PTR> (dosHeader);
+    DWORD_PTR dwPIHeaders = dwDosHeaders + (DWORD) (dosHeader->e_lfanew);
+
+    PIMAGE_NT_HEADERS pNTHeader =
+            reinterpret_cast<PIMAGE_NT_HEADERS> (dwPIHeaders);
+
+    // Verify that the e_lfanew field gave us a reasonable
+    // pointer and the PE signature.
+    // TODO: To really fix JDK-8131321 this condition needs to be changed.
+    // There is a matching change
+    // in JavaVirtualMachine.cpp that also needs to be changed.
+    if (pNTHeader->Signature == IMAGE_NT_SIGNATURE) {
+        DWORD base = (DWORD) (dwDosHeaders);
+        result = GetImportsSection(base, pNTHeader);
+    }
+
+    return result;
+}
+
+#include <TlHelp32.h>
+
+WindowsJob::WindowsJob() {
+    FHandle = NULL;
+}
+
+WindowsJob::~WindowsJob() {
+    if (FHandle != NULL) {
+        CloseHandle(FHandle);
+    }
+}
+
+HANDLE WindowsJob::GetHandle() {
+    if (FHandle == NULL) {
+        FHandle = CreateJobObject(NULL, NULL); // GLOBAL
+
+        if (FHandle == NULL) {
+            ::MessageBox(0, _T("Could not create job object"),
+                    _T("TEST"), MB_OK);
+        } else {
+            JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli = {0};
+
+            // Configure all child processes associated with
+            // the job to terminate when the
+            jeli.BasicLimitInformation.LimitFlags =
+                    JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
+            if (0 == SetInformationJobObject(FHandle,
+                    JobObjectExtendedLimitInformation, &jeli, sizeof (jeli))) {
+                ::MessageBox(0, _T("Could not SetInformationJobObject"),
+                        _T("TEST"), MB_OK);
+            }
+        }
+    }
+
+    return FHandle;
+}
+
+// Initialize static member of WindowsProcess
+WindowsJob WindowsProcess::FJob;
+
+WindowsProcess::WindowsProcess() : Process() {
+    FRunning = false;
+}
+
+WindowsProcess::~WindowsProcess() {
+    Terminate();
+}
+
+void WindowsProcess::Cleanup() {
+    CloseHandle(FProcessInfo.hProcess);
+    CloseHandle(FProcessInfo.hThread);
+}
+
+bool WindowsProcess::IsRunning() {
+    bool result = false;
+
+    HANDLE handle = ::CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0);
+    if (handle == INVALID_HANDLE_VALUE) {
+        return false;
+    }
+
+    PROCESSENTRY32 process = {0};
+    process.dwSize = sizeof (process);
+
+    if (::Process32First(handle, &process)) {
+        do {
+            if (process.th32ProcessID == FProcessInfo.dwProcessId) {
+                result = true;
+                break;
+            }
+        } while (::Process32Next(handle, &process));
+    }
+
+    CloseHandle(handle);
+
+    return result;
+}
+
+bool WindowsProcess::Terminate() {
+    bool result = false;
+
+    if (IsRunning() == true && FRunning == true) {
+        FRunning = false;
+    }
+
+    return result;
+}
+
+bool WindowsProcess::Execute(const TString Application,
+        const std::vector<TString> Arguments, bool AWait) {
+    bool result = false;
+
+    if (FRunning == false) {
+        FRunning = true;
+
+        STARTUPINFO startupInfo;
+        ZeroMemory(&startupInfo, sizeof (startupInfo));
+        startupInfo.cb = sizeof (startupInfo);
+        ZeroMemory(&FProcessInfo, sizeof (FProcessInfo));
+
+        TString command = Application;
+
+        for (std::vector<TString>::const_iterator iterator = Arguments.begin();
+                iterator != Arguments.end(); iterator++) {
+            command += TString(_T(" ")) + *iterator;
+        }
+
+        if (::CreateProcess(Application.data(), (wchar_t*)command.data(), NULL,
+                NULL, FALSE, 0, NULL, NULL, &startupInfo, &FProcessInfo) == FALSE) {
+            TString message = PlatformString::Format(
+                    _T("Error: Unable to create process %s"),
+                    Application.data());
+            throw Exception(message);
+        } else {
+            if (FJob.GetHandle() != NULL) {
+                if (::AssignProcessToJobObject(FJob.GetHandle(),
+                        FProcessInfo.hProcess) == 0) {
+                    // Failed to assign process to job. It doesn't prevent
+                    // anything from continuing so continue.
+                }
+            }
+
+            // Wait until child process exits.
+            if (AWait == true) {
+                Wait();
+                // Close process and thread handles.
+                Cleanup();
+            }
+        }
+    }
+
+    return result;
+}
+
+bool WindowsProcess::Wait() {
+    bool result = false;
+
+    WaitForSingleObject(FProcessInfo.hProcess, INFINITE);
+    return result;
+}
+
+TProcessID WindowsProcess::GetProcessID() {
+    return FProcessInfo.dwProcessId;
+}
+
+bool WindowsProcess::ReadOutput() {
+    bool result = false;
+    // TODO implement
+    return result;
+}
+
+void WindowsProcess::SetInput(TString Value) {
+    // TODO implement
+}
+
+std::list<TString> WindowsProcess::GetOutput() {
+    ReadOutput();
+    return Process::GetOutput();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/libapplauncher/WindowsPlatform.h	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2014, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef WINDOWSPLATFORM_H
+#define WINDOWSPLATFORM_H
+
+#include <Windows.h>
+#include "Platform.h"
+
+class WindowsPlatform : virtual public Platform {
+private:
+    DWORD FMainThread;
+
+public:
+    WindowsPlatform(void);
+    virtual ~WindowsPlatform(void);
+
+    virtual TCHAR* ConvertStringToFileSystemString(TCHAR* Source,
+            bool &release);
+    virtual TCHAR* ConvertFileSystemStringToString(TCHAR* Source,
+            bool &release);
+
+    virtual void ShowMessage(TString title, TString description);
+    virtual void ShowMessage(TString description);
+    virtual MessageResponse ShowResponseMessage(TString title,
+            TString description);
+
+    virtual void SetCurrentDirectory(TString Value);
+    virtual TString GetPackageRootDirectory();
+    virtual TString GetAppDataDirectory();
+    virtual TString GetAppName();
+    virtual TString GetBundledJVMLibraryFileName(TString RuntimePath);
+    TString GetPackageAppDirectory();
+    TString GetPackageLauncherDirectory();
+    TString GetPackageRuntimeBinDirectory();
+
+    virtual ISectionalPropertyContainer* GetConfigFile(TString FileName);
+
+    virtual TString GetModuleFileName();
+    virtual Module LoadLibrary(TString FileName);
+    virtual void FreeLibrary(Module AModule);
+    virtual Procedure GetProcAddress(Module AModule, std::string MethodName);
+
+    virtual Process* CreateProcess();
+
+    virtual bool IsMainThread();
+    virtual TPlatformNumber GetMemorySize();
+
+    virtual TString GetTempDirectory();
+    void InitStreamLocale(wios *stream);
+    void addPlatformDependencies(JavaLibrary *pJavaLibrary);
+};
+
+class FileHandle {
+private:
+    HANDLE FHandle;
+
+public:
+    FileHandle(std::wstring FileName);
+    ~FileHandle();
+
+    bool IsValid();
+    HANDLE GetHandle();
+};
+
+
+class FileMappingHandle {
+private:
+    HANDLE FHandle;
+
+public:
+    FileMappingHandle(HANDLE FileHandle);
+    ~FileMappingHandle();
+
+    bool IsValid();
+    HANDLE GetHandle();
+};
+
+
+class FileData {
+private:
+    LPVOID FBaseAddress;
+
+public:
+    FileData(HANDLE Handle);
+    ~FileData();
+
+    bool IsValid();
+    LPVOID GetBaseAddress();
+};
+
+
+class WindowsLibrary {
+private:
+    TString FFileName;
+
+    // Given an RVA, look up the section header that encloses it and return a
+    // pointer to its IMAGE_SECTION_HEADER
+    static PIMAGE_SECTION_HEADER GetEnclosingSectionHeader(DWORD rva,
+            PIMAGE_NT_HEADERS pNTHeader);
+    static LPVOID GetPtrFromRVA(DWORD rva, PIMAGE_NT_HEADERS pNTHeader,
+            DWORD imageBase);
+    static std::vector<TString> GetImportsSection(DWORD base,
+            PIMAGE_NT_HEADERS pNTHeader);
+    static std::vector<TString> DumpPEFile(PIMAGE_DOS_HEADER dosHeader);
+
+public:
+    WindowsLibrary(const TString FileName);
+
+    std::vector<TString> GetImports();
+};
+
+
+class WindowsJob {
+private:
+    HANDLE FHandle;
+
+public:
+    WindowsJob();
+    ~WindowsJob();
+
+    HANDLE GetHandle();
+};
+
+
+class WindowsProcess : public Process {
+private:
+    bool FRunning;
+
+    PROCESS_INFORMATION FProcessInfo;
+    static WindowsJob FJob;
+
+    void Cleanup();
+    bool ReadOutput();
+
+public:
+    WindowsProcess();
+    virtual ~WindowsProcess();
+
+    virtual bool IsRunning();
+    virtual bool Terminate();
+    virtual bool Execute(const TString Application,
+            const std::vector<TString> Arguments, bool AWait = false);
+    virtual bool Wait();
+    virtual TProcessID GetProcessID();
+    virtual void SetInput(TString Value);
+    virtual std::list<TString> GetOutput();
+};
+
+#endif // WINDOWSPLATFORM_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/libjpackage/ByteBuffer.cpp	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2015, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "ByteBuffer.h"
+
+#include <stdio.h>
+
+ByteBuffer::ByteBuffer() {
+    buffer.reserve(1024);
+}
+
+ByteBuffer::~ByteBuffer() {
+}
+
+LPBYTE ByteBuffer::getPtr() {
+    return &buffer[0];
+}
+
+size_t ByteBuffer::getPos() {
+    return buffer.size();
+}
+
+void ByteBuffer::AppendString(wstring str) {
+    size_t len = (str.size() + 1) * sizeof (WCHAR);
+    AppendBytes((BYTE*) str.c_str(), len);
+}
+
+void ByteBuffer::AppendWORD(WORD word) {
+    AppendBytes((BYTE*) & word, sizeof (WORD));
+}
+
+void ByteBuffer::Align(size_t bytesNumber) {
+    size_t pos = getPos();
+    if (pos % bytesNumber) {
+        DWORD dwNull = 0;
+        size_t len = bytesNumber - pos % bytesNumber;
+        AppendBytes((BYTE*) & dwNull, len);
+    }
+}
+
+void ByteBuffer::AppendBytes(BYTE* ptr, size_t len) {
+    buffer.insert(buffer.end(), ptr, ptr + len);
+}
+
+void ByteBuffer::ReplaceWORD(size_t offset, WORD word) {
+    ReplaceBytes(offset, (BYTE*) & word, sizeof (WORD));
+}
+
+void ByteBuffer::ReplaceBytes(size_t offset, BYTE* ptr, size_t len) {
+    for (size_t i = 0; i < len; i++) {
+        buffer[offset + i] = *(ptr + i);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/libjpackage/ByteBuffer.h	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2015, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef BYTEBUFFER_H
+#define BYTEBUFFER_H
+
+#include <windows.h>
+#include <vector>
+#include <string>
+
+using namespace std;
+
+class ByteBuffer {
+public:
+    ByteBuffer();
+    ~ByteBuffer();
+
+    LPBYTE getPtr();
+    size_t getPos();
+
+    void AppendString(wstring str);
+    void AppendWORD(WORD word);
+    void AppendBytes(BYTE* ptr, size_t len);
+
+    void ReplaceWORD(size_t offset, WORD word);
+    void ReplaceBytes(size_t offset, BYTE* ptr, size_t len);
+
+    void Align(size_t bytesNumber);
+
+private:
+    vector<BYTE> buffer;
+};
+
+#endif // BYTEBUFFER_H
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/libjpackage/IconSwap.cpp	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,203 @@
+/*
+ * Copyright (c) 2012, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include <stdio.h>
+#include <windows.h>
+#include <stdlib.h>
+#include <string>
+#include <malloc.h>
+
+using namespace std;
+
+// http://msdn.microsoft.com/en-us/library/ms997538.aspx
+
+typedef struct _ICONDIRENTRY {
+    BYTE bWidth;
+    BYTE bHeight;
+    BYTE bColorCount;
+    BYTE bReserved;
+    WORD wPlanes;
+    WORD wBitCount;
+    DWORD dwBytesInRes;
+    DWORD dwImageOffset;
+} ICONDIRENTRY, * LPICONDIRENTRY;
+
+typedef struct _ICONDIR {
+    WORD idReserved;
+    WORD idType;
+    WORD idCount;
+    ICONDIRENTRY idEntries[1];
+} ICONDIR, * LPICONDIR;
+
+// #pragmas are used here to insure that the structure's
+// packing in memory matches the packing of the EXE or DLL.
+#pragma pack(push)
+#pragma pack(2)
+
+typedef struct _GRPICONDIRENTRY {
+    BYTE bWidth;
+    BYTE bHeight;
+    BYTE bColorCount;
+    BYTE bReserved;
+    WORD wPlanes;
+    WORD wBitCount;
+    DWORD dwBytesInRes;
+    WORD nID;
+} GRPICONDIRENTRY, * LPGRPICONDIRENTRY;
+#pragma pack(pop)
+
+#pragma pack(push)
+#pragma pack(2)
+
+typedef struct _GRPICONDIR {
+    WORD idReserved;
+    WORD idType;
+    WORD idCount;
+    GRPICONDIRENTRY idEntries[1];
+} GRPICONDIR, * LPGRPICONDIR;
+#pragma pack(pop)
+
+void PrintError() {
+    LPVOID message = NULL;
+    DWORD error = GetLastError();
+
+    if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
+            FORMAT_MESSAGE_IGNORE_INSERTS, NULL, error,
+            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+            (LPTSTR) & message, 0, NULL) != 0) {
+        printf("%S", (LPTSTR) message);
+        LocalFree(message);
+    }
+}
+
+// Note: We do not check here that iconTarget is valid icon.
+// Java code will already do this for us.
+
+bool ChangeIcon(wstring iconTarget, wstring launcher) {
+    WORD language = MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT);
+
+    HANDLE icon = CreateFile(iconTarget.c_str(), GENERIC_READ, 0, NULL,
+            OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+    if (icon == INVALID_HANDLE_VALUE) {
+        PrintError();
+        return false;
+    }
+
+    // Reading .ICO file
+    WORD idReserved, idType, idCount;
+
+    DWORD dwBytesRead;
+    ReadFile(icon, &idReserved, sizeof (WORD), &dwBytesRead, NULL);
+    ReadFile(icon, &idType, sizeof (WORD), &dwBytesRead, NULL);
+    ReadFile(icon, &idCount, sizeof (WORD), &dwBytesRead, NULL);
+
+    LPICONDIR lpid = (LPICONDIR) malloc(
+            sizeof (ICONDIR) + (sizeof (ICONDIRENTRY) * (idCount - 1)));
+    if (lpid == NULL) {
+        CloseHandle(icon);
+        printf("Error: Failed to allocate memory\n");
+        return false;
+    }
+
+    lpid->idReserved = idReserved;
+    lpid->idType = idType;
+    lpid->idCount = idCount;
+
+    ReadFile(icon, &lpid->idEntries[0], sizeof (ICONDIRENTRY) * lpid->idCount,
+            &dwBytesRead, NULL);
+
+    LPGRPICONDIR lpgid = (LPGRPICONDIR) malloc(
+            sizeof (GRPICONDIR) + (sizeof (GRPICONDIRENTRY) * (idCount - 1)));
+    if (lpid == NULL) {
+        CloseHandle(icon);
+        free(lpid);
+        printf("Error: Failed to allocate memory\n");
+        return false;
+    }
+
+    lpgid->idReserved = idReserved;
+    lpgid->idType = idType;
+    lpgid->idCount = idCount;
+
+    for (int i = 0; i < lpgid->idCount; i++) {
+        lpgid->idEntries[i].bWidth = lpid->idEntries[i].bWidth;
+        lpgid->idEntries[i].bHeight = lpid->idEntries[i].bHeight;
+        lpgid->idEntries[i].bColorCount = lpid->idEntries[i].bColorCount;
+        lpgid->idEntries[i].bReserved = lpid->idEntries[i].bReserved;
+        lpgid->idEntries[i].wPlanes = lpid->idEntries[i].wPlanes;
+        lpgid->idEntries[i].wBitCount = lpid->idEntries[i].wBitCount;
+        lpgid->idEntries[i].dwBytesInRes = lpid->idEntries[i].dwBytesInRes;
+        lpgid->idEntries[i].nID = i + 1;
+    }
+
+    // Store images in .EXE
+    HANDLE update = BeginUpdateResource(launcher.c_str(), FALSE);
+    if (update == NULL) {
+        free(lpid);
+        free(lpgid);
+        CloseHandle(icon);
+        PrintError();
+        return false;
+    }
+
+    for (int i = 0; i < lpid->idCount; i++) {
+        LPBYTE lpBuffer = (LPBYTE) malloc(lpid->idEntries[i].dwBytesInRes);
+        SetFilePointer(icon, lpid->idEntries[i].dwImageOffset,
+                NULL, FILE_BEGIN);
+        ReadFile(icon, lpBuffer, lpid->idEntries[i].dwBytesInRes,
+                &dwBytesRead, NULL);
+        if (!UpdateResource(update, RT_ICON,
+                MAKEINTRESOURCE(lpgid->idEntries[i].nID),
+                language, &lpBuffer[0], lpid->idEntries[i].dwBytesInRes)) {
+            free(lpBuffer);
+            free(lpid);
+            free(lpgid);
+            CloseHandle(icon);
+            PrintError();
+            return false;
+        }
+        free(lpBuffer);
+    }
+
+    free(lpid);
+    CloseHandle(icon);
+
+    if (!UpdateResource(update, RT_GROUP_ICON,
+            MAKEINTRESOURCE(1), language, &lpgid[0],
+            (sizeof (WORD) * 3) + (sizeof (GRPICONDIRENTRY) * lpgid->idCount))) {
+        free(lpgid);
+        PrintError();
+        return false;
+    }
+
+    free(lpgid);
+
+    if (EndUpdateResource(update, FALSE) == FALSE) {
+        PrintError();
+        return false;
+    }
+
+    return true;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/libjpackage/IconSwap.h	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef ICONSWAP_H
+#define ICONSWAP_H
+
+#include <string>
+
+using namespace std;
+
+bool ChangeIcon(wstring iconTarget, wstring launcher);
+
+#endif // ICONSWAP_H
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/libjpackage/Utils.cpp	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "Windows.h"
+#include "Utils.h"
+
+#define BUFFER_SIZE 4096
+
+wstring GetStringFromJString(JNIEnv *pEnv, jstring jstr) {
+    const jchar *pJChars = pEnv->GetStringChars(jstr, NULL);
+    if (pJChars == NULL) {
+        return wstring(L"");
+    }
+
+    wstring wstr(pJChars);
+
+    pEnv->ReleaseStringChars(jstr, pJChars);
+
+    return wstr;
+}
+
+jstring GetJStringFromString(JNIEnv *pEnv, const jchar *unicodeChars, jsize len) {
+    return pEnv->NewString(unicodeChars, len);
+}
+
+wstring GetLongPath(wstring path) {
+    wstring result(L"");
+
+    size_t len = path.length();
+    if (len > 1) {
+        if (path.at(len - 1) == '\\') {
+            path.erase(len - 1);
+        }
+    }
+
+    TCHAR *pBuffer = new TCHAR[BUFFER_SIZE];
+    if (pBuffer != NULL) {
+        DWORD dwResult = GetLongPathName(path.c_str(), pBuffer, BUFFER_SIZE);
+        if (dwResult > 0 && dwResult < BUFFER_SIZE) {
+            result = wstring(pBuffer);
+        } else {
+            delete [] pBuffer;
+            pBuffer = new TCHAR[dwResult];
+            if (pBuffer != NULL) {
+                DWORD dwResult2 = GetLongPathName(path.c_str(), pBuffer, dwResult);
+                if (dwResult2 == (dwResult - 1)) {
+                    result = wstring(pBuffer);
+                }
+            }
+        }
+
+        if (pBuffer != NULL) {
+            delete [] pBuffer;
+        }
+    }
+
+    return result;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/libjpackage/Utils.h	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef UTILS_H
+#define UTILS_H
+
+#include <string>
+#include "jni.h"
+
+using namespace std;
+
+wstring GetStringFromJString(JNIEnv *pEnv, jstring jstr);
+jstring GetJStringFromString(JNIEnv *pEnv, const jchar *unicodeChars, jsize len);
+
+wstring GetLongPath(wstring path);
+
+#endif // UTILS_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/libjpackage/VersionInfoSwap.cpp	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,284 @@
+/*
+ * Copyright (c) 2015, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "VersionInfoSwap.h"
+
+#include <stdio.h>
+#include <tchar.h>
+
+#include <windows.h>
+#include <stdio.h>
+#include <Strsafe.h>
+#include <fstream>
+#include <locale>
+#include <codecvt>
+
+using namespace std;
+
+/*
+ * [Property file] contains key/value pairs
+ * The swap tool uses these pairs to create new version resource
+ *
+ * See MSDN docs for VS_VERSIONINFO structure that
+ * depicts organization of data in this version resource
+ *    https://msdn.microsoft.com/en-us/library/ms647001(v=vs.85).aspx
+ *
+ * The swap tool makes changes in [Executable file]
+ * The tool assumes that the executable file has no version resource
+ * and it adds new resource in the executable file.
+ * If the executable file has an existing version resource, then
+ * the existing version resource will be replaced with new one.
+ */
+
+VersionInfoSwap::VersionInfoSwap(wstring executableProperties, wstring launcher) {
+    m_executableProperties = executableProperties;
+    m_launcher = launcher;
+}
+
+bool VersionInfoSwap::PatchExecutable() {
+    bool b = LoadFromPropertyFile();
+    if (!b) {
+        return false;
+    }
+
+    ByteBuffer buf;
+    b = CreateNewResource(&buf);
+    if (!b) {
+        return false;
+    }
+
+    b = this->UpdateResource(buf.getPtr(), static_cast<DWORD> (buf.getPos()));
+    if (!b) {
+        return false;
+    }
+
+    return true;
+}
+
+bool VersionInfoSwap::LoadFromPropertyFile() {
+    wifstream stream(m_executableProperties.c_str());
+
+    const locale empty_locale = locale::empty();
+    const locale utf8_locale =
+            locale(empty_locale, new codecvt_utf8<wchar_t>());
+    stream.imbue(utf8_locale);
+
+    if (stream.is_open() == true) {
+        int lineNumber = 1;
+        while (stream.eof() == false) {
+            wstring line;
+            getline(stream, line);
+
+            // # at the first character will comment out the line.
+            if (line.empty() == false && line[0] != '#') {
+                wstring::size_type pos = line.find('=');
+                if (pos != wstring::npos) {
+                    wstring name = line.substr(0, pos);
+                    wstring value = line.substr(pos + 1);
+                    m_props[name] = value;
+                }
+            }
+            lineNumber++;
+        }
+        return true;
+    }
+
+    return false;
+}
+
+/*
+ * Creates new version resource
+ *
+ * MSND docs for VS_VERSION_INFO structure
+ *     https://msdn.microsoft.com/en-us/library/ms647001(v=vs.85).aspx
+ */
+bool VersionInfoSwap::CreateNewResource(ByteBuffer *buf) {
+    size_t versionInfoStart = buf->getPos();
+    buf->AppendWORD(0);
+    buf->AppendWORD(sizeof VS_FIXEDFILEINFO);
+    buf->AppendWORD(0);
+    buf->AppendString(TEXT("VS_VERSION_INFO"));
+    buf->Align(4);
+
+    VS_FIXEDFILEINFO fxi;
+    if (!FillFixedFileInfo(&fxi)) {
+        return false;
+    }
+    buf->AppendBytes((BYTE*) & fxi, sizeof (VS_FIXEDFILEINFO));
+    buf->Align(4);
+
+    // String File Info
+    size_t stringFileInfoStart = buf->getPos();
+    buf->AppendWORD(0);
+    buf->AppendWORD(0);
+    buf->AppendWORD(1);
+    buf->AppendString(TEXT("StringFileInfo"));
+    buf->Align(4);
+
+    // String Table
+    size_t stringTableStart = buf->getPos();
+    buf->AppendWORD(0);
+    buf->AppendWORD(0);
+    buf->AppendWORD(1);
+
+    // "040904B0" = LANG_ENGLISH/SUBLANG_ENGLISH_US, Unicode CP
+    buf->AppendString(TEXT("040904B0"));
+    buf->Align(4);
+
+    // Strings
+    vector<wstring> keys;
+    for (map<wstring, wstring>::const_iterator it =
+            m_props.begin(); it != m_props.end(); ++it) {
+        keys.push_back(it->first);
+    }
+
+    for (size_t index = 0; index < keys.size(); index++) {
+        wstring name = keys[index];
+        wstring value = m_props[name];
+
+        size_t stringStart = buf->getPos();
+        buf->AppendWORD(0);
+        buf->AppendWORD(static_cast<WORD> (value.length()));
+        buf->AppendWORD(1);
+        buf->AppendString(name);
+        buf->Align(4);
+        buf->AppendString(value);
+        buf->ReplaceWORD(stringStart,
+                static_cast<WORD> (buf->getPos() - stringStart));
+        buf->Align(4);
+    }
+
+    buf->ReplaceWORD(stringTableStart,
+            static_cast<WORD> (buf->getPos() - stringTableStart));
+    buf->ReplaceWORD(stringFileInfoStart,
+            static_cast<WORD> (buf->getPos() - stringFileInfoStart));
+
+    // VarFileInfo
+    size_t varFileInfoStart = buf->getPos();
+    buf->AppendWORD(1);
+    buf->AppendWORD(0);
+    buf->AppendWORD(1);
+    buf->AppendString(TEXT("VarFileInfo"));
+    buf->Align(4);
+
+    buf->AppendWORD(0x24);
+    buf->AppendWORD(0x04);
+    buf->AppendWORD(0x00);
+    buf->AppendString(TEXT("Translation"));
+    buf->Align(4);
+    // "000004B0" = LANG_NEUTRAL/SUBLANG_ENGLISH_US, Unicode CP
+    buf->AppendWORD(0x0000);
+    buf->AppendWORD(0x04B0);
+
+    buf->ReplaceWORD(varFileInfoStart,
+            static_cast<WORD> (buf->getPos() - varFileInfoStart));
+    buf->ReplaceWORD(versionInfoStart,
+            static_cast<WORD> (buf->getPos() - versionInfoStart));
+
+    return true;
+}
+
+bool VersionInfoSwap::FillFixedFileInfo(VS_FIXEDFILEINFO *fxi) {
+    wstring fileVersion;
+    wstring productVersion;
+    int ret;
+
+    fileVersion = m_props[TEXT("FileVersion")];
+    productVersion = m_props[TEXT("ProductVersion")];
+
+    unsigned fv_1 = 0, fv_2 = 0, fv_3 = 0, fv_4 = 0;
+    unsigned pv_1 = 0, pv_2 = 0, pv_3 = 0, pv_4 = 0;
+
+    ret = _stscanf_s(fileVersion.c_str(),
+            TEXT("%d.%d.%d.%d"), &fv_1, &fv_2, &fv_3, &fv_4);
+    if (ret <= 0 || ret > 4) {
+        return false;
+    }
+
+    ret = _stscanf_s(productVersion.c_str(),
+            TEXT("%d.%d.%d.%d"), &pv_1, &pv_2, &pv_3, &pv_4);
+    if (ret <= 0 || ret > 4) {
+        return false;
+    }
+
+    fxi->dwSignature = 0xFEEF04BD;
+    fxi->dwStrucVersion = 0x00010000;
+
+    fxi->dwFileVersionMS = MAKELONG(fv_2, fv_1);
+    fxi->dwFileVersionLS = MAKELONG(fv_4, fv_3);
+    fxi->dwProductVersionMS = MAKELONG(pv_2, pv_1);
+    fxi->dwProductVersionLS = MAKELONG(pv_4, pv_3);
+
+    fxi->dwFileFlagsMask = 0;
+    fxi->dwFileFlags = 0;
+    fxi->dwFileOS = VOS_NT_WINDOWS32;
+
+    wstring exeExt =
+            m_launcher.substr(m_launcher.find_last_of(TEXT(".")));
+    if (exeExt == TEXT(".exe")) {
+        fxi->dwFileType = VFT_APP;
+    } else if (exeExt == TEXT(".dll")) {
+        fxi->dwFileType = VFT_DLL;
+    } else {
+        fxi->dwFileType = VFT_UNKNOWN;
+    }
+    fxi->dwFileSubtype = 0;
+
+    fxi->dwFileDateLS = 0;
+    fxi->dwFileDateMS = 0;
+
+    return true;
+}
+
+/*
+ * Adds new resource in the executable
+ */
+bool VersionInfoSwap::UpdateResource(LPVOID lpResLock, DWORD size) {
+
+    HANDLE hUpdateRes;
+    BOOL r;
+
+    hUpdateRes = ::BeginUpdateResource(m_launcher.c_str(), FALSE);
+    if (hUpdateRes == NULL) {
+        return false;
+    }
+
+    r = ::UpdateResource(hUpdateRes,
+            RT_VERSION,
+            MAKEINTRESOURCE(VS_VERSION_INFO),
+            MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
+            lpResLock,
+            size);
+
+    if (!r) {
+        return false;
+    }
+
+    if (!::EndUpdateResource(hUpdateRes, FALSE)) {
+        return false;
+    }
+
+    return true;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/libjpackage/VersionInfoSwap.h	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2015, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef VERSIONINFOSWAP_H
+#define VERSIONINFOSWAP_H
+
+#include "ByteBuffer.h"
+#include <map>
+
+using namespace std;
+
+class VersionInfoSwap {
+public:
+    VersionInfoSwap(wstring executableProperties, wstring launcher);
+
+    bool PatchExecutable();
+
+private:
+    wstring m_executableProperties;
+    wstring m_launcher;
+
+    map<wstring, wstring> m_props;
+
+    bool LoadFromPropertyFile();
+    bool CreateNewResource(ByteBuffer *buf);
+    bool UpdateResource(LPVOID lpResLock, DWORD size);
+    bool FillFixedFileInfo(VS_FIXEDFILEINFO *fxi);
+};
+
+#endif // VERSIONINFOSWAP_H
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/libjpackage/WindowsRegistry.cpp	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include <Windows.h>
+#include <strsafe.h>
+#include <tchar.h>
+#include <jni.h>
+
+#include "Utils.h"
+
+// Max value name size per MSDN plus NULL
+#define VALUE_NAME_SIZE 16384
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#undef jdk_jpackage_internal_WindowsRegistry_HKEY_LOCAL_MACHINE
+#define jdk_jpackage_internal_WindowsRegistry_HKEY_LOCAL_MACHINE 1L
+
+    /*
+     * Class:     jdk_jpackage_internal_WindowsRegistry
+     * Method:    readDwordValue
+     * Signature: (ILjava/lang/String;Ljava/lang/String;I)I
+     */
+    JNIEXPORT jint JNICALL Java_jdk_jpackage_internal_WindowsRegistry_readDwordValue(
+            JNIEnv *pEnv, jclass c, jint key, jstring jSubKey, jstring jValue, jint defaultValue) {
+        jint jResult = defaultValue;
+
+        if (key != jdk_jpackage_internal_WindowsRegistry_HKEY_LOCAL_MACHINE) {
+            return jResult;
+        }
+
+        wstring subKey = GetStringFromJString(pEnv, jSubKey);
+        wstring value = GetStringFromJString(pEnv, jValue);
+
+        HKEY hSubKey = NULL;
+        LSTATUS status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, subKey.c_str(), 0,
+                KEY_QUERY_VALUE, &hSubKey);
+        if (status == ERROR_SUCCESS) {
+            DWORD dwValue = 0;
+            DWORD cbData = sizeof (DWORD);
+            status = RegQueryValueEx(hSubKey, value.c_str(), NULL, NULL,
+                    (LPBYTE) & dwValue, &cbData);
+            if (status == ERROR_SUCCESS) {
+                jResult = (jint) dwValue;
+            }
+
+            RegCloseKey(hSubKey);
+        }
+
+        return jResult;
+    }
+
+    /*
+     * Class:     jdk_jpackage_internal_WindowsRegistry
+     * Method:    openRegistryKey
+     * Signature: (ILjava/lang/String;)J
+     */
+    JNIEXPORT jlong JNICALL Java_jdk_jpackage_internal_WindowsRegistry_openRegistryKey(
+            JNIEnv *pEnv, jclass c, jint key, jstring jSubKey) {
+        if (key != jdk_jpackage_internal_WindowsRegistry_HKEY_LOCAL_MACHINE) {
+            return 0;
+        }
+
+        wstring subKey = GetStringFromJString(pEnv, jSubKey);
+        HKEY hSubKey = NULL;
+        LSTATUS status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, subKey.c_str(), 0,
+                KEY_QUERY_VALUE, &hSubKey);
+        if (status == ERROR_SUCCESS) {
+            return (jlong)hSubKey;
+        }
+
+        return 0;
+    }
+
+    /*
+     * Class:     jdk_jpackage_internal_WindowsRegistry
+     * Method:    enumRegistryValue
+     * Signature: (JI)Ljava/lang/String;
+     */
+    JNIEXPORT jstring JNICALL Java_jdk_jpackage_internal_WindowsRegistry_enumRegistryValue(
+            JNIEnv *pEnv, jclass c, jlong lKey, jint jIndex) {
+        HKEY hKey = (HKEY)lKey;
+        TCHAR valueName[VALUE_NAME_SIZE] = {0}; // Max value name size per MSDN plus NULL
+        DWORD cchValueName = VALUE_NAME_SIZE;
+        LSTATUS status = RegEnumValue(hKey, (DWORD)jIndex, valueName, &cchValueName,
+                                      NULL, NULL, NULL, NULL);
+        if (status == ERROR_SUCCESS) {
+            size_t chLength = 0;
+            if (StringCchLength(valueName, VALUE_NAME_SIZE, &chLength) == S_OK) {
+                return GetJStringFromString(pEnv, valueName, (jsize)chLength);
+            }
+        }
+
+        return NULL;
+    }
+
+    /*
+     * Class:     jdk_jpackage_internal_WindowsRegistry
+     * Method:    closeRegistryKey
+     * Signature: (J)V
+     */
+    JNIEXPORT void JNICALL Java_jdk_jpackage_internal_WindowsRegistry_closeRegistryKey(
+            JNIEnv *pEnc, jclass c, jlong lKey) {
+        HKEY hKey = (HKEY)lKey;
+        RegCloseKey(hKey);
+    }
+
+    /*
+     * Class:     jdk_jpackage_internal_WindowsRegistry
+     * Method:    comparePaths
+     * Signature: (Ljava/lang/String;Ljava/lang/String;)Z
+     */
+     JNIEXPORT jboolean JNICALL Java_jdk_jpackage_internal_WindowsRegistry_comparePaths(
+             JNIEnv *pEnv, jclass c, jstring jPath1, jstring jPath2) {
+         wstring path1 = GetStringFromJString(pEnv, jPath1);
+         wstring path2 = GetStringFromJString(pEnv, jPath2);
+
+         path1 = GetLongPath(path1);
+         path2 = GetLongPath(path2);
+
+         if (path1.length() == 0 || path2.length() == 0) {
+             return JNI_FALSE;
+         }
+
+         if (path1.length() != path2.length()) {
+             return JNI_FALSE;
+         }
+
+         if (_tcsnicmp(path1.c_str(), path2.c_str(), path1.length()) == 0) {
+             return JNI_TRUE;
+         }
+
+         return JNI_FALSE;
+     }
+
+#ifdef __cplusplus
+}
+#endif
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/native/libjpackage/jpackage.cpp	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2011, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string>
+#include <windows.h>
+
+#include "IconSwap.h"
+#include "VersionInfoSwap.h"
+#include "Utils.h"
+
+using namespace std;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+    /*
+     * Class:     jdk_jpackage_internal_WindowsAppImageBuilder
+     * Method:    iconSwap
+     * Signature: (Ljava/lang/String;Ljava/lang/String;)I
+     */
+    JNIEXPORT jint JNICALL Java_jdk_jpackage_internal_WindowsAppImageBuilder_iconSwap(
+            JNIEnv *pEnv, jclass c, jstring jIconTarget, jstring jLauncher) {
+        wstring iconTarget = GetStringFromJString(pEnv, jIconTarget);
+        wstring launcher = GetStringFromJString(pEnv, jLauncher);
+
+        if (ChangeIcon(iconTarget, launcher)) {
+            return 0;
+        }
+
+        return 1;
+    }
+
+    /*
+     * Class:     jdk_jpackage_internal_WindowsAppImageBuilder
+     * Method:    versionSwap
+     * Signature: (Ljava/lang/String;Ljava/lang/String;)I
+     */
+    JNIEXPORT jint JNICALL Java_jdk_jpackage_internal_WindowsAppImageBuilder_versionSwap(
+            JNIEnv *pEnv, jclass c, jstring jExecutableProperties, jstring jLauncher) {
+
+        wstring executableProperties = GetStringFromJString(pEnv, jExecutableProperties);
+        wstring launcher = GetStringFromJString(pEnv, jLauncher);
+
+        VersionInfoSwap vs(executableProperties, launcher);
+        if (vs.PatchExecutable()) {
+            return 0;
+        }
+
+        return 1;
+    }
+
+    BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
+        return TRUE;
+    }
+
+#ifdef __cplusplus
+}
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/JPackageHelpTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+ /*
+ * @test
+ * @summary jpackage help test
+ * @library helpers
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageHelpTest
+ */
+public class JPackageHelpTest {
+
+    // Platform specific help messages.
+    private static final String WINDOWS_HELP =
+            "The following options are valid for Windows platforms";
+    private static final String OSX_HELP =
+            "The following options are valid for Mac OS X platforms";
+    private static final String LINUX_HELP =
+            "The following options are valid for Linux platforms";
+
+    private static void validate(String output1, String output2)
+            throws Exception {
+        if (output1.split("\n").length < 25) {
+            throw new AssertionError("jpacakger --help failed");
+        }
+
+        if (output2.split("\n").length < 25) {
+            throw new AssertionError("jpacakger -h failed");
+        }
+
+        // Make sure output matches for --help and -h
+        if (!output1.equals(output2)) {
+            System.err.println("================= --help =================");
+            System.err.println(output1);
+
+            System.err.println("=================== -h ===================");
+            System.err.println(output2);
+
+            throw new AssertionError(
+                    "jpacakger help text does not match between --help and -h");
+        }
+
+        if (JPackageHelper.isWindows()) {
+            if (!output1.contains(WINDOWS_HELP)) {
+                throw new AssertionError(
+                  "jpacakger help text missing Windows specific help");
+            }
+
+            if (output1.contains(OSX_HELP) || output1.contains(LINUX_HELP)) {
+                throw new AssertionError(
+                  "jpacakger help text contains other platforms specific help");
+
+            }
+        } else if (JPackageHelper.isOSX()) {
+            if (!output1.contains(OSX_HELP)) {
+                throw new AssertionError(
+                  "jpacakger help text missing OS X specific help");
+            }
+
+            if (output1.contains(WINDOWS_HELP) ||
+                    output1.contains(LINUX_HELP)) {
+                throw new AssertionError(
+                 "jpacakger help text contains other platforms specific help");
+            }
+        } else if (JPackageHelper.isLinux()) {
+            if (!output1.contains(LINUX_HELP)) {
+                throw new AssertionError(
+                  "jpacakger help text missing Linux specific help");
+            }
+
+            if (output1.contains(OSX_HELP) || output1.contains(WINDOWS_HELP)) {
+                throw new AssertionError(
+                  "jpacakger help text contains other platforms specific help");
+            }
+        }
+    }
+
+    private static void testHelp() throws Exception {
+        String output1 = JPackageHelper.executeCLI(true, "--help");
+        String output2 = JPackageHelper.executeCLI(true, "-h");
+        validate(output1, output2);
+    }
+
+    private static void testHelpToolProvider() throws Exception {
+        String output1 = JPackageHelper.executeToolProvider(true, "--help");
+        String output2 = JPackageHelper.executeToolProvider(true, "-h");
+        validate(output1, output2);
+    }
+
+    public static void main(String[] args) throws Exception {
+        testHelp();
+        testHelpToolProvider();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/JPackageInvalidArgTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+ /*
+ * @test
+ * @summary jpackage invalid argument test
+ * @library helpers
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageInvalidArgTest
+ */
+public class JPackageInvalidArgTest {
+
+    private static final String ARG1 = "--no-such-argument";
+    private static final String ARG2 = "--overwrite";
+    private static final String RESULT1 =
+            "Invalid Option: [--no-such-argument]";
+    private static final String RESULT2 = "Mode is not specified";
+
+    private static void validate(String arg, String output) throws Exception {
+        String[] result = output.split("\n");
+        if (result.length != 1) {
+            System.err.println(output);
+            throw new AssertionError("Invalid number of lines in output: "
+                    + result.length);
+        }
+
+        if (arg.equals(ARG1)) {
+            if (!result[0].trim().contains(RESULT1)) {
+                System.err.println("Expected: " + RESULT1);
+                System.err.println("Actual: " + result[0]);
+                throw new AssertionError("Unexpected output: " + result[0]);
+            }
+        } else if (arg.equals(ARG2)) {
+            if (!result[0].trim().contains(RESULT2)) {
+                System.err.println("Expected: " + RESULT2);
+                System.err.println("Actual: " + result[0]);
+                throw new AssertionError("Unexpected output: " + result[0]);
+            }
+        }
+    }
+
+    private static void testInvalidArg() throws Exception {
+        String output = JPackageHelper.executeCLI(false, ARG1);
+        validate(ARG1, output);
+        output = JPackageHelper.executeCLI(false, ARG2);
+        validate(ARG2, output);
+    }
+
+    private static void testInvalidArgToolProvider() throws Exception {
+        String output = JPackageHelper.executeToolProvider(false, ARG1);
+        validate(ARG1, output);
+        output = JPackageHelper.executeToolProvider(false, ARG2);
+        validate(ARG2, output);
+    }
+
+    public static void main(String[] args) throws Exception {
+        testInvalidArg();
+        testInvalidArgToolProvider();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/JPackageMissingArgumentsTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+ /*
+ * @test
+ * @summary jpackage create image missing arguments test
+ * @library helpers
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageMissingArgumentsTest
+ */
+
+public class JPackageMissingArgumentsTest {
+    private static final String [] RESULT_1 = {"--output"};
+    private static final String [] CMD_1 = {
+        "create-image",
+        "--input", "input",
+        "--name", "test",
+        "--main-jar", "hello.jar",
+        "--main-class", "Hello",
+        "--overwrite",
+        "--files", "hello.jar"};
+
+    private static final String [] RESULT_2 = {"--input"};
+    private static final String [] CMD_2 = {
+        "create-image",
+        "--output", "output",
+        "--name", "test",
+        "--main-jar", "hello.jar",
+        "--main-class", "Hello",
+        "--overwrite",
+        "--files", "hello.jar"};
+
+    private static final String [] RESULT_3 = {"--input", "--app-image"};
+    private static final String [] CMD_3 = {
+        "create-installer",
+        "--output", "output",
+        "--name", "test",
+        "--main-jar", "hello.jar",
+        "--main-class", "Hello",
+        "--overwrite",
+        "--files", "hello.jar"};
+
+    private static final String [] RESULT_4 = {"--main-class"};
+    private static final String [] CMD_4 = {
+        "create-image",
+        "--input", "input",
+        "--output", "output",
+        "--name", "test",
+        "--main-jar", "hello.jar",
+        "--overwrite",
+        "--files", "hello.jar"};
+
+    private static final String [] RESULT_5 = {"--main-jar"};
+    private static final String [] CMD_5 = {
+        "create-image",
+        "--input", "input",
+        "--output", "output",
+        "--name", "test",
+        "--main-class", "Hello",
+        "--overwrite",
+        "--files", "hello.jar"};
+
+    private static final String [] RESULT_6 = {"--module-path", "--runtime-image"};
+    private static final String [] CMD_6 = {
+        "create-image",
+        "--output", "output",
+        "--name", "test",
+        "--module", "com.hello/com.hello.Hello",
+        "--overwrite",
+        "--files", "hello.jar"};
+
+    private static final String [] RESULT_7 = {"--module-path", "--runtime-image",
+                                               "--app-image"};
+    private static final String [] CMD_7 = {
+        "create-installer",
+        "--output", "output",
+        "--name", "test",
+        "--module", "com.hello/com.hello.Hello",
+        "--overwrite",
+        "--files", "hello.jar"};
+
+    private static void validate(String output, String [] expected)
+           throws Exception {
+        String[] result = output.split("\n");
+        if (result.length != 1) {
+            System.err.println(output);
+            throw new AssertionError("Invalid number of lines in output: "
+                    + result.length);
+        }
+
+        for (String s : expected) {
+            if (!result[0].contains(s)) {
+                System.err.println("Expected to contain: " + s);
+                System.err.println("Actual: " + result[0]);
+                throw new AssertionError("Unexpected error message");
+            }
+        }
+    }
+
+    private static void testMissingArg() throws Exception {
+        String output = JPackageHelper.executeCLI(false, CMD_1);
+        validate(output, RESULT_1);
+
+        output = JPackageHelper.executeCLI(false, CMD_2);
+        validate(output, RESULT_2);
+
+        output = JPackageHelper.executeCLI(false, CMD_3);
+        validate(output, RESULT_3);
+
+        output = JPackageHelper.executeCLI(false, CMD_4);
+        validate(output, RESULT_4);
+
+        output = JPackageHelper.executeCLI(false, CMD_5);
+        validate(output, RESULT_5);
+
+        output = JPackageHelper.executeCLI(false, CMD_6);
+        validate(output, RESULT_6);
+
+        output = JPackageHelper.executeCLI(false, CMD_7);
+        validate(output, RESULT_7);
+    }
+
+    private static void testMissingArgToolProvider() throws Exception {
+        String output = JPackageHelper.executeToolProvider(false, CMD_1);
+        validate(output, RESULT_1);
+
+        output = JPackageHelper.executeToolProvider(false, CMD_2);
+        validate(output, RESULT_2);
+
+        output = JPackageHelper.executeToolProvider(false, CMD_3);
+        validate(output, RESULT_3);
+
+        output = JPackageHelper.executeToolProvider(false, CMD_4);
+        validate(output, RESULT_4);
+
+        output = JPackageHelper.executeToolProvider(false, CMD_5);
+        validate(output, RESULT_5);
+
+        output = JPackageHelper.executeToolProvider(false, CMD_6);
+        validate(output, RESULT_6);
+
+        output = JPackageHelper.executeToolProvider(false, CMD_7);
+        validate(output, RESULT_7);
+    }
+
+    public static void main(String[] args) throws Exception {
+        JPackageHelper.createHelloImageJar();
+        testMissingArg();
+        testMissingArgToolProvider();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/JPackageNoArgTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+ /*
+ * @test
+ * @summary jpackage no argument test
+ * @library helpers
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageNoArgTest
+ */
+public class JPackageNoArgTest {
+
+    private static final String RESULT1 = "Usage: jpackage <mode> <options>";
+    private static final String[] EXPECTED =
+            {"--help", "list of possible options"};
+
+    private static void validate(String output) throws Exception {
+        String[] result = output.split("\n");
+        if (result.length != 2) {
+            System.err.println(output);
+            throw new AssertionError(
+                    "Invalid number of lines in output: " + result.length);
+        }
+
+        if (!result[0].trim().equals(RESULT1)) {
+            System.err.println("Expected: " + RESULT1);
+            System.err.println("Actual: " + result[0]);
+            throw new AssertionError("Unexpected line 1");
+        }
+
+        for (String expected : EXPECTED) {
+            if (!result[1].contains(expected)) {
+                System.err.println("Expected to contain: " + expected);
+                System.err.println("Actual: " + result[1]);
+                throw new AssertionError("Unexpected line 2");
+            }
+        }
+    }
+
+    private static void testNoArg() throws Exception {
+        String output = JPackageHelper.executeCLI(true, new String[0]);
+        validate(output);
+    }
+
+    private static void testNoArgToolProvider() throws Exception {
+        String output =
+                JPackageHelper.executeToolProvider(true, new String[0]);
+        validate(output);
+    }
+
+    public static void main(String[] args) throws Exception {
+        testNoArg();
+        testNoArgToolProvider();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/JPackageVersionTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+ /*
+ * @test
+ * @summary jpackage version test
+ * @library helpers
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageVersionTest
+ */
+public class JPackageVersionTest {
+
+    private static final String ARG = "--version";
+    private static final String RESULT = "jpackage version"
+            + " " + System.getProperty("java.version");
+
+    private static void validate(String output) throws Exception {
+        String[] result = output.split("\n");
+        if (result.length != 1) {
+            System.err.println(output);
+            throw new AssertionError("Invalid number of lines in output: " + result.length);
+        }
+
+        if (!result[0].trim().equals(RESULT)) {
+            System.err.println("Expected: " + RESULT);
+            System.err.println("Actual: " + result[0]);
+            throw new AssertionError("Unexpected line 1");
+        }
+    }
+
+    private static void testVersion() throws Exception {
+        String output = JPackageHelper.executeCLI(true, ARG);
+        validate(output);
+    }
+
+    private static void testVersionToolProvider() throws Exception {
+        String output = JPackageHelper.executeToolProvider(true, ARG);
+        validate(output);
+    }
+
+    public static void main(String[] args) throws Exception {
+        testVersion();
+        testVersionToolProvider();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/apps/com.hello/com/hello/Hello.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.hello;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.PrintWriter;
+
+public class Hello {
+
+    private static final String MSG = "jpackage test application";
+    private static final int EXPECTED_NUM_OF_PARAMS = 3; // Starts at 1
+
+    public static void main(String[] args) {
+        String outputFile = "appOutput.txt";
+        File file = new File(outputFile);
+
+        try (PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(file)))) {
+            System.out.println(MSG);
+            out.println(MSG);
+
+            System.out.println("args.length: " + args.length);
+            out.println("args.length: " + args.length);
+
+            for (String arg : args) {
+                System.out.println(arg);
+                out.println(arg);
+            }
+
+            for (int index = 1; index <= EXPECTED_NUM_OF_PARAMS; index++) {
+                String value = System.getProperty("param" + index);
+                if (value != null) {
+                    System.out.println("-Dparam" + index + "=" + value);
+                    out.println("-Dparam" + index + "=" + value);
+                }
+            }
+        } catch (Exception ex) {
+            System.err.println(ex.toString());
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/apps/com.hello/module-info.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+module com.hello {
+    exports com.hello;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/apps/image/Hello.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.PrintWriter;
+
+public class Hello {
+
+    private static final String MSG = "jpackage test application";
+    private static final int EXPECTED_NUM_OF_PARAMS = 3; // Starts at 1
+
+    public static void main(String[] args) {
+        printToStdout(args);
+        printToFile(args);
+    }
+
+    private static void printToStdout(String[] args) {
+        System.out.println(MSG);
+
+        System.out.println("args.length: " + args.length);
+
+        for (String arg : args) {
+            System.out.println(arg);
+        }
+
+        for (int index = 1; index <= EXPECTED_NUM_OF_PARAMS; index++) {
+            String value = System.getProperty("param" + index);
+            if (value != null) {
+                System.out.println("-Dparam" + index + "=" + value);
+            }
+        }
+    }
+
+    private static void printToFile(String[] args) {
+        String outputFile = "appOutput.txt";
+        File file = new File(outputFile);
+
+        try (PrintWriter out
+                = new PrintWriter(new BufferedWriter(new FileWriter(file)))) {
+            out.println(MSG);
+
+            out.println("args.length: " + args.length);
+
+            for (String arg : args) {
+                out.println(arg);
+            }
+
+            for (int index = 1; index <= EXPECTED_NUM_OF_PARAMS; index++) {
+                String value = System.getProperty("param" + index);
+                if (value != null) {
+                    out.println("-Dparam" + index + "=" + value);
+                }
+            }
+        } catch (Exception ex) {
+            System.err.println(ex.getMessage());
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/apps/installer/Hello.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.PrintWriter;
+
+public class Hello {
+
+    private static final String MSG = "jpackage test application";
+    private static final int EXPECTED_NUM_OF_PARAMS = 3; // Starts at 1
+
+    public static void main(String[] args) {
+        printToStdout(args);
+        if (args.length == 1) { // Called via file association
+            printToFile(args);
+        }
+    }
+
+    private static void printToStdout(String[] args) {
+        System.out.println(MSG);
+
+        System.out.println("args.length: " + args.length);
+
+        for (String arg : args) {
+            System.out.println(arg);
+        }
+
+        for (int index = 1; index <= EXPECTED_NUM_OF_PARAMS; index++) {
+            String value = System.getProperty("param" + index);
+            if (value != null) {
+                System.out.println("-Dparam" + index + "=" + value);
+            }
+        }
+    }
+
+    private static void printToFile(String[] args) {
+        File inputFile = new File(args[0]);
+        String outputFile = inputFile.getParent() + File.separator + "appOutput.txt";
+        File file = new File(outputFile);
+
+        try (PrintWriter out
+                = new PrintWriter(new BufferedWriter(new FileWriter(file)))) {
+            out.println(MSG);
+
+            out.println("args.length: " + args.length);
+
+            for (String arg : args) {
+                out.println(arg);
+            }
+
+            for (int index = 1; index <= EXPECTED_NUM_OF_PARAMS; index++) {
+                String value = System.getProperty("param" + index);
+                if (value != null) {
+                    out.println("-Dparam" + index + "=" + value);
+                }
+            }
+        } catch (Exception ex) {
+            System.err.println(ex.getMessage());
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createimage/JPackageCreateImageArgumentsBase.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.File;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.List;
+
+public class JPackageCreateImageArgumentsBase {
+
+    private static final String app = JPackagePath.getApp();
+    private static final String appOutput = JPackagePath.getAppOutputFile();
+    private static final String appWorkingDir = JPackagePath.getAppWorkingDir();
+
+    private static final String ARGUMENT1 = "argument";
+    private static final String ARGUMENT2 = "Some Arguments";
+    private static final String ARGUMENT3 = "Value \"with\" quotes";
+
+    private static final String ARGUMENT_CMD1 = "test";
+
+    private static final List<String> arguments = new ArrayList<>();
+    private static final List<String> argumentsCmd = new ArrayList<>();
+
+    public static void initArguments(boolean toolProvider, String[] cmd) {
+        if (arguments.isEmpty()) {
+            arguments.add(ARGUMENT1);
+            arguments.add(ARGUMENT2);
+            arguments.add(ARGUMENT3);
+        }
+
+        if (argumentsCmd.isEmpty()) {
+            argumentsCmd.add(ARGUMENT_CMD1);
+        }
+
+        String argumentsMap
+                = JPackageHelper.listToArgumentsMap(arguments, toolProvider);
+        cmd[cmd.length - 1] = argumentsMap;
+    }
+
+    private static void validateResult(String[] result, List<String> args)
+            throws Exception {
+        if (result.length != (args.size() + 2)) {
+            throw new AssertionError(
+                    "Unexpected number of lines: " + result.length);
+        }
+
+        if (!result[0].trim().equals("jpackage test application")) {
+            throw new AssertionError("Unexpected result[0]: " + result[0]);
+        }
+
+        if (!result[1].trim().equals("args.length: " + args.size())) {
+            throw new AssertionError("Unexpected result[1]: " + result[1]);
+        }
+
+        int index = 2;
+        for (String arg : args) {
+            if (!result[index].trim().equals(arg)) {
+                throw new AssertionError(
+                        "Unexpected result[" + index + "]: " + result[index]);
+            }
+            index++;
+        }
+    }
+
+    private static void validate(String arg, List<String> expectedArgs)
+            throws Exception {
+        int retVal;
+
+        if (arg == null) {
+            retVal = JPackageHelper.execute(null, app);
+        } else {
+            retVal = JPackageHelper.execute(null, app, arg);
+        }
+        if (retVal != 0) {
+            throw new AssertionError("Test application exited with error: "
+                    + retVal);
+        }
+
+        File outfile = new File(appWorkingDir + File.separator + appOutput);
+        if (!outfile.exists()) {
+            throw new AssertionError(appOutput + " was not created");
+        }
+
+        String output = Files.readString(outfile.toPath());
+        String[] result = output.split("\n");
+        validateResult(result, expectedArgs);
+    }
+
+    public static void testCreateImage(String[] cmd) throws Exception {
+        initArguments(false, cmd);
+        JPackageHelper.executeCLI(true, cmd);
+        validate(null, arguments);
+        validate(ARGUMENT_CMD1, argumentsCmd);
+    }
+
+    public static void testCreateImageToolProvider(String[] cmd) throws Exception {
+        initArguments(true, cmd);
+        JPackageHelper.executeToolProvider(true, cmd);
+        validate(null, arguments);
+        validate(ARGUMENT_CMD1, argumentsCmd);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createimage/JPackageCreateImageArgumentsModuleTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary jpackage create image with --arguments test
+ * @library ../helpers
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageCreateImageArgumentsBase
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageCreateImageArgumentsModuleTest
+ */
+public class JPackageCreateImageArgumentsModuleTest {
+
+    private static final String[] CMD = {
+        "create-image",
+        "--output", "output",
+        "--name", "test",
+        "--overwrite",
+        "--module", "com.hello/com.hello.Hello",
+        "--module-path", "input",
+        "--arguments", "TBD"};
+
+    public static void main(String[] args) throws Exception {
+        JPackageHelper.createHelloModule();
+        JPackageCreateImageArgumentsBase.testCreateImage(CMD);
+        JPackageCreateImageArgumentsBase.testCreateImageToolProvider(CMD);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createimage/JPackageCreateImageArgumentsTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary jpackage create image with --arguments test
+ * @library ../helpers
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageCreateImageArgumentsBase
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageCreateImageArgumentsTest
+ */
+public class JPackageCreateImageArgumentsTest {
+
+    private static final String[] CMD = {
+        "create-image",
+        "--input", "input",
+        "--output", "output",
+        "--name", "test",
+        "--main-jar", "hello.jar",
+        "--main-class", "Hello",
+        "--files", "hello.jar",
+        "--overwrite",
+        "--arguments", "TBD"};
+
+    public static void main(String[] args) throws Exception {
+        JPackageHelper.createHelloImageJar();
+        JPackageCreateImageArgumentsBase.testCreateImage(CMD);
+        JPackageCreateImageArgumentsBase.testCreateImageToolProvider(CMD);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createimage/JPackageCreateImageBase.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.File;
+import java.nio.file.Files;
+
+public abstract class JPackageCreateImageBase {
+    private static final String app = JPackagePath.getApp();
+    private static final String appOutput = JPackagePath.getAppOutputFile();
+    private static final String appWorkingDir = JPackagePath.getAppWorkingDir();
+
+    private static void validateResult(String[] result) throws Exception {
+        if (result.length != 2) {
+            throw new AssertionError(
+                   "Unexpected number of lines: " + result.length);
+        }
+
+        if (!result[0].trim().equals("jpackage test application")) {
+            throw new AssertionError("Unexpected result[0]: " + result[0]);
+        }
+
+        if (!result[1].trim().equals("args.length: 0")) {
+            throw new AssertionError("Unexpected result[1]: " + result[1]);
+        }
+    }
+
+    private static void validate() throws Exception {
+        int retVal = JPackageHelper.execute(null, app);
+        if (retVal != 0) {
+            throw new AssertionError(
+                   "Test application exited with error: " + retVal);
+        }
+
+        File outfile = new File(appWorkingDir + File.separator + appOutput);
+        if (!outfile.exists()) {
+            throw new AssertionError(appOutput + " was not created");
+        }
+
+        String output = Files.readString(outfile.toPath());
+        String[] result = output.split("\n");
+        validateResult(result);
+    }
+
+    public static void testCreateImage(String [] cmd) throws Exception {
+        JPackageHelper.executeCLI(true, cmd);
+        validate();
+    }
+
+    public static void testCreateImageToolProvider(String [] cmd) throws Exception {
+        JPackageHelper.executeToolProvider(true, cmd);
+        validate();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createimage/JPackageCreateImageBuildRootTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.File;
+
+ /*
+ * @test
+ * @requires (os.family == "windows")
+ * @summary jpackage create image to test --build-root
+ * @library ../helpers
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageCreateImageBuildRootTest
+ */
+public class JPackageCreateImageBuildRootTest {
+    private static String buildRoot = null;
+    private static final String BUILD_ROOT = "buildRoot";
+    private static final String BUILD_ROOT_TB = "buildRootToolProvider";
+
+    private static final String [] CMD = {
+        "create-image",
+        "--input", "input",
+        "--output", "output",
+        "--name", "test",
+        "--main-jar", "hello.jar",
+        "--main-class", "Hello",
+        "--files", "hello.jar",
+        "--overwrite" };
+
+    private static final String [] CMD_BUILD_ROOT = {
+        "create-image",
+        "--input", "input",
+        "--output", "output",
+        "--name", "test",
+        "--main-jar", "hello.jar",
+        "--main-class", "Hello",
+        "--files", "hello.jar",
+        "--overwrite",
+        "--build-root", "TBD"};
+
+    private static void validate(boolean retain) throws Exception {
+        File br = new File(buildRoot);
+        if (retain) {
+            if (!br.exists()) {
+                throw new AssertionError(br.getAbsolutePath() + " does not exist");
+            }
+        } else {
+            if (br.exists()) {
+                throw new AssertionError(br.getAbsolutePath() + " exist");
+            }
+        }
+    }
+
+    private static void init(boolean toolProvider) {
+        if (toolProvider) {
+            buildRoot = BUILD_ROOT_TB;
+        } else {
+            buildRoot = BUILD_ROOT;
+        }
+
+        CMD_BUILD_ROOT[CMD_BUILD_ROOT.length - 1] = buildRoot;
+    }
+
+    private static void testBuildRoot() throws Exception {
+        init(false);
+        JPackageHelper.executeCLI(true, CMD);
+        validate(false);
+        JPackageHelper.executeCLI(true, CMD_BUILD_ROOT);
+        validate(true);
+    }
+
+    private static void testBuildRootToolProvider() throws Exception {
+        init(true);
+        JPackageHelper.executeToolProvider(true, CMD);
+        validate(false);
+        JPackageHelper.executeToolProvider(true, CMD_BUILD_ROOT);
+        validate(true);
+    }
+
+    public static void main(String[] args) throws Exception {
+        JPackageHelper.createHelloImageJar();
+        testBuildRoot();
+        testBuildRootToolProvider();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createimage/JPackageCreateImageIconTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.File;
+import java.nio.file.Files;
+
+/*
+ * @test
+ * @summary jpackage create image to verify --icon
+ * @library ../helpers
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageCreateImageIconTest
+ */
+public class JPackageCreateImageIconTest {
+    private static final String app = JPackagePath.getApp();
+    private static final String appOutput = JPackagePath.getAppOutputFile();
+    private static final String appWorkingDir = JPackagePath.getAppWorkingDir();
+
+    private static final String[] CMD = {
+        "create-image",
+        "--input", "input",
+        "--name", "test",
+        "--main-jar", "hello.jar",
+        "--main-class", "Hello",
+        "--overwrite",
+        "--files", "hello.jar",
+        "--icon", getIconPath(),
+        "--output", "output"};
+
+    private static void validateResult(String[] result) throws Exception {
+        if (result.length != 2) {
+            throw new AssertionError(
+                   "Unexpected number of lines: " + result.length);
+        }
+
+        if (!result[0].trim().equals("jpackage test application")) {
+            throw new AssertionError("Unexpected result[0]: " + result[0]);
+        }
+
+        if (!result[1].trim().equals("args.length: 0")) {
+            throw new AssertionError("Unexpected result[1]: " + result[1]);
+        }
+    }
+
+    private static void validate() throws Exception {
+        int retVal = JPackageHelper.execute(null, app);
+        if (retVal != 0) {
+            throw new AssertionError(
+                   "Test application exited with error: " + retVal);
+        }
+
+        File outfile = new File(appWorkingDir + File.separator + appOutput);
+        if (!outfile.exists()) {
+            throw new AssertionError(appOutput + " was not created");
+        }
+
+        String output = Files.readString(outfile.toPath());
+        String[] result = output.split("\n");
+        validateResult(result);
+    }
+
+    private static void validateIcon() throws Exception {
+        File origIcon = new File(getIconPath());
+        File icon = new File(JPackagePath.getAppIcon());
+        if (origIcon.length() != icon.length()) {
+            System.err.println("origIcon.length(): " + origIcon.length());
+            System.err.println("icon.length(): " + icon.length());
+            throw new AssertionError("Icons size does not match");
+        }
+    }
+
+    private static void testIcon() throws Exception {
+        JPackageHelper.executeCLI(true, CMD);
+        validate();
+        validateIcon();
+    }
+
+    private static void testIconToolProvider() throws Exception {
+        JPackageHelper.executeToolProvider(true, CMD);
+        validate();
+        validateIcon();
+    }
+
+    private static String getIconPath() {
+        String ext = ".ico";
+        if (JPackageHelper.isOSX()) {
+            ext = ".icns";
+        } else if (JPackageHelper.isLinux()) {
+            ext = ".png";
+        }
+
+        String path = JPackagePath.getTestSrcRoot() + File.separator + "resources"
+                + File.separator + "icon" + ext;
+
+        return path;
+    }
+
+    public static void main(String[] args) throws Exception {
+        JPackageHelper.createHelloImageJar();
+        testIcon();
+        testIconToolProvider();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createimage/JPackageCreateImageInputFilesTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.PrintWriter;
+
+ /*
+ * @test
+ * @summary jpackage create image input/files test
+ * @library ../helpers
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageCreateImageInputFilesTest
+ */
+public class JPackageCreateImageInputFilesTest {
+    private static final String inputFile =
+            "input" + File.separator + "input.txt";
+    private static final String jarFile =
+            "input" + File.separator + "hello.jar";
+    private static final String appInputFilePath;
+    private static final String appJarFilePath;
+
+    static {
+        appInputFilePath = JPackagePath.getAppWorkingDir() + File.separator + "input.txt";
+        appJarFilePath = JPackagePath.getAppWorkingDir() + File.separator + "hello.jar";
+    }
+
+    private static final String [] CMD_1 = {
+        "create-image",
+        "--input", "input",
+        "--output", "output",
+        "--name", "test",
+        "--main-jar", "hello.jar",
+        "--overwrite",
+        "--main-class", "Hello"};
+
+    private static final String [] CMD_2 = {
+        "create-image",
+        "--input", "input",
+        "--output", "output",
+        "--name", "test",
+        "--main-jar", "hello.jar",
+        "--main-class", "Hello",
+        "--overwrite",
+        "--files", "hello.jar"};
+
+    private static void validate1() throws Exception {
+        File input = new File(appInputFilePath);
+        if (!input.exists()) {
+            throw new AssertionError("Unexpected file does not exist: "
+                    + input.getAbsolutePath());
+        }
+
+        File jar = new File(appJarFilePath);
+        if (!jar.exists()) {
+            throw new AssertionError("Unexpected file does not exist: "
+                    + jar.getAbsolutePath());
+        }
+    }
+
+    private static void validate2() throws Exception {
+        File input = new File(appInputFilePath);
+        if (input.exists()) {
+            throw new AssertionError("Unexpected file exist: "
+                    + input.getAbsolutePath());
+        }
+
+        File jar = new File(appJarFilePath);
+        if (!jar.exists()) {
+            throw new AssertionError("Unexpected file does not exist: "
+                    + jar.getAbsolutePath());
+        }
+    }
+
+    private static void testCreateImage() throws Exception {
+        JPackageHelper.executeCLI(true, CMD_1);
+        validate1();
+
+        JPackageHelper.executeCLI(true, CMD_2);
+        validate2();
+    }
+
+    private static void testCreateImageToolProvider() throws Exception {
+        JPackageHelper.executeToolProvider(true, CMD_1);
+        validate1();
+
+        JPackageHelper.executeToolProvider(true, CMD_2);
+        validate2();
+    }
+
+    private static void createInputFile() throws Exception {
+        try (PrintWriter out = new PrintWriter(
+                new BufferedWriter(new FileWriter(inputFile)))) {
+            out.println("jpackgaer resource file");
+        }
+    }
+
+    public static void main(String[] args) throws Exception {
+        JPackageHelper.createHelloImageJar();
+
+        createInputFile();
+
+        testCreateImage();
+        testCreateImageToolProvider();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createimage/JPackageCreateImageJVMArgsBase.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.File;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.List;
+
+public class JPackageCreateImageJVMArgsBase {
+
+    private static final String app = JPackagePath.getApp();
+    private static final String appOutput = JPackagePath.getAppOutputFile();
+    private static final String appWorkingDir = JPackagePath.getAppWorkingDir();
+
+    private static final String ARGUMENT1 = "-Dparam1=Some Param 1";
+    private static final String ARGUMENT2 = "-Dparam2=Some \"Param\" 2";
+    private static final String ARGUMENT3 =
+            "-Dparam3=Some \"Param\" with \" 3";
+
+    private static final List<String> arguments = new ArrayList<>();
+
+    private static void initArguments(boolean toolProvider, String [] cmd) {
+        if (arguments.isEmpty()) {
+            arguments.add(ARGUMENT1);
+            arguments.add(ARGUMENT2);
+            arguments.add(ARGUMENT3);
+        }
+
+        String argumentsMap = JPackageHelper.listToArgumentsMap(arguments,
+                toolProvider);
+        cmd[cmd.length - 1] = argumentsMap;
+    }
+
+    private static void initArguments2(boolean toolProvider, String [] cmd) {
+        int index = cmd.length - 6;
+
+        cmd[index++] = "--jvm-args";
+        arguments.clear();
+        arguments.add(ARGUMENT1);
+        cmd[index++] = JPackageHelper.listToArgumentsMap(arguments,
+                toolProvider);
+
+        cmd[index++] = "--jvm-args";
+        arguments.clear();
+        arguments.add(ARGUMENT2);
+        cmd[index++] = JPackageHelper.listToArgumentsMap(arguments,
+                toolProvider);
+
+        cmd[index++] = "--jvm-args";
+        arguments.clear();
+        arguments.add(ARGUMENT3);
+        cmd[index++] = JPackageHelper.listToArgumentsMap(arguments,
+                toolProvider);
+
+        arguments.clear();
+        arguments.add(ARGUMENT1);
+        arguments.add(ARGUMENT2);
+        arguments.add(ARGUMENT3);
+    }
+
+    private static void validateResult(String[] result, List<String> args)
+            throws Exception {
+        if (result.length != (args.size() + 2)) {
+            for (String r : result) {
+                System.err.println(r.trim());
+            }
+            throw new AssertionError("Unexpected number of lines: "
+                    + result.length);
+        }
+
+        if (!result[0].trim().equals("jpackage test application")) {
+            throw new AssertionError("Unexpected result[0]: " + result[0]);
+        }
+
+        if (!result[1].trim().equals("args.length: 0")) {
+            throw new AssertionError("Unexpected result[1]: " + result[1]);
+        }
+
+        int index = 2;
+        for (String arg : args) {
+            if (!result[index].trim().equals(arg)) {
+                throw new AssertionError("Unexpected result[" + index + "]: "
+                    + result[index]);
+            }
+            index++;
+        }
+    }
+
+    private static void validate(List<String> expectedArgs) throws Exception {
+        int retVal = JPackageHelper.execute(null, app);
+        if (retVal != 0) {
+            throw new AssertionError("Test application exited with error: "
+                    + retVal);
+        }
+
+        File outfile = new File(appWorkingDir + File.separator + appOutput);
+        if (!outfile.exists()) {
+            throw new AssertionError(appOutput + " was not created");
+        }
+
+        String output = Files.readString(outfile.toPath());
+        String[] result = output.split("\n");
+        validateResult(result, expectedArgs);
+    }
+
+    public static void testCreateImageJVMArgs(String [] cmd) throws Exception {
+        initArguments(false, cmd);
+        JPackageHelper.executeCLI(true, cmd);
+        validate(arguments);
+    }
+
+    public static void testCreateImageJVMArgsToolProvider(String [] cmd) throws Exception {
+        initArguments(true, cmd);
+        JPackageHelper.executeToolProvider(true, cmd);
+        validate(arguments);
+    }
+
+    public static void testCreateImageJVMArgs2(String [] cmd) throws Exception {
+        initArguments2(false, cmd);
+        JPackageHelper.executeCLI(true, cmd);
+        validate(arguments);
+    }
+
+    public static void testCreateImageJVMArgs2ToolProvider(String [] cmd) throws Exception {
+        initArguments2(true, cmd);
+        JPackageHelper.executeToolProvider(true, cmd);
+        validate(arguments);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createimage/JPackageCreateImageJVMArgsModuleTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary jpackage create image with --jvm-args test
+ * @library ../helpers
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageCreateImageJVMArgsBase
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageCreateImageJVMArgsModuleTest
+ */
+public class JPackageCreateImageJVMArgsModuleTest {
+
+    private static final String[] CMD = {
+        "create-image",
+        "--output", "output",
+        "--name", "test",
+        "--overwrite",
+        "--module", "com.hello/com.hello.Hello",
+        "--module-path", "input",
+        "--jvm-args", "TBD"};
+
+    private static final String[] CMD2 = {
+        "create-image",
+        "--output", "output",
+        "--name", "test",
+        "--overwrite",
+        "--module", "com.hello/com.hello.Hello",
+        "--module-path", "input",
+        "--jvm-args", "TBD",
+        "--jvm-args", "TBD",
+        "--jvm-args", "TBD"};
+
+    public static void main(String[] args) throws Exception {
+        JPackageHelper.createHelloModule();
+
+        JPackageCreateImageJVMArgsBase.testCreateImageJVMArgs(CMD);
+        JPackageCreateImageJVMArgsBase.testCreateImageJVMArgsToolProvider(CMD);
+
+        JPackageCreateImageJVMArgsBase.testCreateImageJVMArgs2(CMD2);
+        JPackageCreateImageJVMArgsBase.testCreateImageJVMArgs2ToolProvider(CMD2);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createimage/JPackageCreateImageJVMArgsTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary jpackage create image with --jvm-args test
+ * @library ../helpers
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageCreateImageJVMArgsBase
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageCreateImageJVMArgsTest
+ */
+public class JPackageCreateImageJVMArgsTest {
+
+    private static final String[] CMD = {
+        "create-image",
+        "--input", "input",
+        "--output", "output",
+        "--name", "test",
+        "--main-jar", "hello.jar",
+        "--main-class", "Hello",
+        "--files", "hello.jar",
+        "--overwrite",
+        "--jvm-args", "TBD"};
+
+    private static final String[] CMD2 = {
+        "create-image",
+        "--input", "input",
+        "--output", "output",
+        "--name", "test",
+        "--main-jar", "hello.jar",
+        "--main-class", "Hello",
+        "--files", "hello.jar",
+        "--overwrite",
+        "--jvm-args", "TBD",
+        "--jvm-args", "TBD",
+        "--jvm-args", "TBD"};
+
+    public static void main(String[] args) throws Exception {
+        JPackageHelper.createHelloImageJar();
+        JPackageCreateImageJVMArgsBase.testCreateImageJVMArgs(CMD);
+        JPackageCreateImageJVMArgsBase.testCreateImageJVMArgsToolProvider(CMD);
+
+        JPackageCreateImageJVMArgsBase.testCreateImageJVMArgs2(CMD2);
+        JPackageCreateImageJVMArgsBase.testCreateImageJVMArgs2ToolProvider(CMD2);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createimage/JPackageCreateImageMainClassAttributeTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.File;
+import java.nio.file.Files;
+
+/*
+ * @test
+ * @summary jpackage create image with no main class arguments and with main-class attribute
+ * @library ../helpers
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageCreateImageMainClassAttributeTest
+ */
+public class JPackageCreateImageMainClassAttributeTest {
+    private static final String app = JPackagePath.getApp();
+    private static final String appOutput = JPackagePath.getAppOutputFile();
+    private static final String appWorkingDir = JPackagePath.getAppWorkingDir();
+
+    private static final String[] CMD = {
+        "create-image",
+        "--input", "input",
+        "--output", "output",
+        "--name", "test",
+        "--main-jar", "hello.jar",
+        "--overwrite",
+        "--files", "hello.jar"};
+
+    private static void validateResult(String[] result) throws Exception {
+        if (result.length != 2) {
+            throw new AssertionError(
+                   "Unexpected number of lines: " + result.length);
+        }
+
+        if (!result[0].trim().equals("jpackage test application")) {
+            throw new AssertionError("Unexpected result[0]: " + result[0]);
+        }
+
+        if (!result[1].trim().equals("args.length: 0")) {
+            throw new AssertionError("Unexpected result[1]: " + result[1]);
+        }
+    }
+
+    private static void validate() throws Exception {
+        int retVal = JPackageHelper.execute(null, app);
+        if (retVal != 0) {
+            throw new AssertionError(
+                   "Test application exited with error: " + retVal);
+        }
+
+        File outfile = new File(appWorkingDir + File.separator + appOutput);
+        if (!outfile.exists()) {
+            throw new AssertionError(appOutput + " was not created");
+        }
+
+        String output = Files.readString(outfile.toPath());
+        String[] result = output.split("\n");
+        validateResult(result);
+    }
+
+    private static void testMainClassAttribute() throws Exception {
+        JPackageHelper.executeCLI(true, CMD);
+        validate();
+    }
+
+    private static void testMainClassAttributeToolProvider() throws Exception {
+        JPackageHelper.executeToolProvider(true, CMD);
+        validate();
+    }
+
+    public static void main(String[] args) throws Exception {
+        JPackageHelper.createHelloImageJarWithMainClass();
+        testMainClassAttribute();
+        testMainClassAttributeToolProvider();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createimage/JPackageCreateImageModularJarTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2019, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+ /*
+ * @test
+ * @summary jpackage create image modular jar test
+ * @library ../helpers
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageCreateImageBase
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageCreateImageModularJarTest
+ */
+public class JPackageCreateImageModularJarTest {
+
+    private static final String [] CMD1 = {
+        "create-image",
+        "--input", "input",
+        "--output", "output",
+        "--name", "test",
+        "--main-jar", "com.hello.jar",
+        "--main-class", "com.hello.Hello",
+        "--files", "com.hello.jar",
+        "--overwrite"
+    };
+
+    private static final String [] CMD2 = {
+        "create-image",
+        "--output", "output",
+        "--name", "test",
+        "--module", "com.hello/com.hello.Hello",
+        "--module-path", "input/com.hello.jar",
+        "--overwrite"
+    };
+
+    public static void main(String[] args) throws Exception {
+        JPackageHelper.createHelloModule();
+
+        JPackageCreateImageBase.testCreateImage(CMD1);
+        JPackageCreateImageBase.testCreateImageToolProvider(CMD1);
+
+        JPackageCreateImageBase.testCreateImage(CMD2);
+        JPackageCreateImageBase.testCreateImageToolProvider(CMD2);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createimage/JPackageCreateImageModuleTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+ /*
+ * @test
+ * @summary jpackage create image module test
+ * @library ../helpers
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageCreateImageBase
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageCreateImageModuleTest
+ */
+public class JPackageCreateImageModuleTest {
+    private static final String [] CMD = {
+        "create-image",
+        "--output", "output",
+        "--name", "test",
+        "--overwrite",
+        "--module", "com.hello/com.hello.Hello",
+        "--module-path", "input"};
+
+    public static void main(String[] args) throws Exception {
+        JPackageHelper.createHelloModule();
+        JPackageCreateImageBase.testCreateImage(CMD);
+        JPackageCreateImageBase.testCreateImageToolProvider(CMD);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createimage/JPackageCreateImageNoNameTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.File;
+import java.nio.file.Files;
+
+/*
+ * @test
+ * @summary jpackage create image with no --name
+ * @library ../helpers
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageCreateImageNoNameTest
+ */
+public class JPackageCreateImageNoNameTest {
+    private static final String app = JPackagePath.getAppNoName();
+    private static final String appOutput = JPackagePath.getAppOutputFile();
+    private static final String appWorkingDir = JPackagePath.getAppWorkingDirNoName();
+
+    private static final String[] CMD = {
+        "create-image",
+        "--input", "input",
+        "--output", "output",
+        "--main-jar", "hello.jar",
+        "--main-class", "Hello",
+        "--overwrite",
+        "--files", "hello.jar"};
+
+    private static void validateResult(String[] result) throws Exception {
+        if (result.length != 2) {
+            throw new AssertionError(
+                   "Unexpected number of lines: " + result.length);
+        }
+
+        if (!result[0].trim().equals("jpackage test application")) {
+            throw new AssertionError("Unexpected result[0]: " + result[0]);
+        }
+
+        if (!result[1].trim().equals("args.length: 0")) {
+            throw new AssertionError("Unexpected result[1]: " + result[1]);
+        }
+    }
+
+    private static void validate() throws Exception {
+        int retVal = JPackageHelper.execute(null, app);
+        if (retVal != 0) {
+            throw new AssertionError(
+                   "Test application exited with error: " + retVal);
+        }
+
+        File outfile = new File(appWorkingDir + File.separator + appOutput);
+        if (!outfile.exists()) {
+            throw new AssertionError(appOutput + " was not created");
+        }
+
+        String output = Files.readString(outfile.toPath());
+        String[] result = output.split("\n");
+        validateResult(result);
+    }
+
+    private static void testMainClassAttribute() throws Exception {
+        JPackageHelper.executeCLI(true, CMD);
+        validate();
+    }
+
+    private static void testMainClassAttributeToolProvider() throws Exception {
+        JPackageHelper.executeToolProvider(true, CMD);
+        validate();
+    }
+
+    public static void main(String[] args) throws Exception {
+        JPackageHelper.createHelloImageJar();
+        testMainClassAttribute();
+        testMainClassAttributeToolProvider();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createimage/JPackageCreateImageOverwriteTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.File;
+import java.nio.file.Files;
+
+/*
+ * @test
+ * @summary jpackage create image to verify --overwrite
+ * @library ../helpers
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageCreateImageOverwriteTest
+ */
+public class JPackageCreateImageOverwriteTest {
+    private static final String app = JPackagePath.getApp();
+    private static final String appOutput = JPackagePath.getAppOutputFile();
+    private static final String appWorkingDir = JPackagePath.getAppWorkingDir();
+
+    private static final String[] CMD = {
+        "create-image",
+        "--input", "input",
+        "--name", "test",
+        "--main-jar", "hello.jar",
+        "--main-class", "Hello",
+        "--files", "hello.jar",
+        "--output", "TBD"};
+
+    private static final String[] CMD_OVERWRITE = {
+        "create-image",
+        "--input", "input",
+        "--name", "test",
+        "--main-jar", "hello.jar",
+        "--main-class", "Hello",
+        "--overwrite",
+        "--files", "hello.jar",
+        "--output", "TBD"};
+
+    private static void validateResult(String[] result) throws Exception {
+        if (result.length != 2) {
+            throw new AssertionError(
+                   "Unexpected number of lines: " + result.length);
+        }
+
+        if (!result[0].trim().equals("jpackage test application")) {
+            throw new AssertionError("Unexpected result[0]: " + result[0]);
+        }
+
+        if (!result[1].trim().equals("args.length: 0")) {
+            throw new AssertionError("Unexpected result[1]: " + result[1]);
+        }
+    }
+
+    private static void validate(String result) throws Exception {
+        if (!result.contains("java.io.IOException") &&
+                !result.contains("already exists") &&
+                !result.contains("--overwrite is not specified")) {
+            System.err.println(result);
+            throw new AssertionError("Unexpected error message");
+        }
+    }
+
+    private static void validateOverwrite() throws Exception {
+        int retVal = JPackageHelper.execute(null, app);
+        if (retVal != 0) {
+            throw new AssertionError(
+                   "Test application exited with error: " + retVal);
+        }
+
+        File outfile = new File(appWorkingDir + File.separator + appOutput);
+        if (!outfile.exists()) {
+            throw new AssertionError(appOutput + " was not created");
+        }
+
+        String output = Files.readString(outfile.toPath());
+        String[] result = output.split("\n");
+        validateResult(result);
+    }
+
+    private static void testOverwrite() throws Exception {
+        CMD[CMD.length - 1] = "output";
+        CMD_OVERWRITE[CMD_OVERWRITE.length - 1] = "output";
+
+        String appFolder = "test";
+        if (JPackageHelper.isOSX()) {
+            appFolder = "test.app";
+        }
+
+        File output = new File("output" + File.separator + appFolder);
+        if (output.exists()) {
+            throw new AssertionError(
+                   "Output folder already exist");
+        }
+        output.mkdirs();
+
+        String result = JPackageHelper.executeCLI(false, CMD);
+        validate(result);
+
+        JPackageHelper.executeCLI(true, CMD_OVERWRITE);
+        validateOverwrite();
+    }
+
+    private static void testOverwriteToolProvider() throws Exception {
+        CMD[CMD.length - 1] = "outputToolProvider";
+        CMD_OVERWRITE[CMD_OVERWRITE.length - 1] = "outputToolProvider";
+
+        String appFolder = "test";
+        if (JPackageHelper.isOSX()) {
+            appFolder = "test.app";
+        }
+
+        File output = new File("outputToolProvider" + File.separator + appFolder);
+        if (output.exists()) {
+            throw new AssertionError(
+                   "Output folder already exist");
+        }
+        output.mkdirs();
+
+        String result = JPackageHelper.executeToolProvider(false, CMD);
+        validate(result);
+
+        JPackageHelper.executeToolProvider(true, CMD_OVERWRITE);
+        validateOverwrite();
+    }
+
+    public static void main(String[] args) throws Exception {
+        JPackageHelper.createHelloImageJar();
+        testOverwrite();
+        testOverwriteToolProvider();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createimage/JPackageCreateImageRuntimeBase.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.File;
+import java.nio.file.Files;
+
+ public class JPackageCreateImageRuntimeBase {
+    private static final String app = JPackagePath.getApp();
+    private static final String appWorkingDir = JPackagePath.getAppWorkingDir();
+    private static final String runtimeJava = JPackagePath.getRuntimeJava();
+    private static final String runtimeJavaOutput = "javaOutput.txt";
+    private static final String appOutput = JPackagePath.getAppOutputFile();
+
+    private static void validateResult(String[] result) throws Exception {
+        if (result.length != 2) {
+            throw new AssertionError("Unexpected number of lines: " + result.length);
+        }
+
+        if (!result[0].trim().equals("jpackage test application")) {
+            throw new AssertionError("Unexpected result[0]: " + result[0]);
+        }
+
+        if (!result[1].trim().equals("args.length: 0")) {
+            throw new AssertionError("Unexpected result[1]: " + result[1]);
+        }
+    }
+
+    private static void validate() throws Exception {
+        int retVal = JPackageHelper.execute(null, app);
+        if (retVal != 0) {
+            throw new AssertionError("Test application exited with error: " + retVal);
+        }
+
+        File outfile = new File(appWorkingDir + File.separator + appOutput);
+        if (!outfile.exists()) {
+            throw new AssertionError(appOutput + " was not created");
+        }
+
+        String output = Files.readString(outfile.toPath());
+        String[] result = output.split("\n");
+        validateResult(result);
+    }
+
+    private static void validateRuntime() throws Exception {
+        int retVal = JPackageHelper.execute(new File(runtimeJavaOutput), runtimeJava, "--list-modules");
+        if (retVal != 0) {
+            throw new AssertionError("Test application exited with error: " + retVal);
+        }
+
+        File outfile = new File(runtimeJavaOutput);
+        if (!outfile.exists()) {
+            throw new AssertionError(runtimeJavaOutput + " was not created");
+        }
+
+        String output = Files.readString(outfile.toPath());
+        String[] result = output.split("\n");
+        if (result.length != 1) {
+            throw new AssertionError("Unexpected number of lines: " + result.length);
+        }
+
+        if (!result[0].startsWith("java.base")) {
+            throw new AssertionError("Unexpected result: " + result[0]);
+        }
+    }
+
+    public static void testCreateImage(String [] cmd) throws Exception {
+        JPackageHelper.executeCLI(true, cmd);
+        validate();
+        validateRuntime();
+    }
+
+    public static void testCreateImageToolProvider(String [] cmd) throws Exception {
+        JPackageHelper.executeToolProvider(true, cmd);
+        validate();
+        validateRuntime();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createimage/JPackageCreateImageRuntimeModuleTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+ /*
+ * @test
+ * @summary jpackage create image runtime test
+ * @library ../helpers
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageCreateImageRuntimeBase
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageCreateImageRuntimeModuleTest
+ */
+public class JPackageCreateImageRuntimeModuleTest {
+    private static final String [] CMD = {
+        "create-image",
+        "--runtime-image", "runtime",
+        "--output", "output",
+        "--name", "test",
+        "--overwrite",
+        "--win-console",
+        "--module", "com.hello/com.hello.Hello",
+        "--module-path", "input"};
+
+    public static void main(String[] args) throws Exception {
+        JPackageHelper.createHelloModule();
+        JPackageHelper.createRuntime();
+        JPackageCreateImageRuntimeBase.testCreateImage(CMD);
+        JPackageCreateImageRuntimeBase.testCreateImageToolProvider(CMD);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createimage/JPackageCreateImageRuntimeTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+ /*
+ * @test
+ * @summary jpackage create image runtime test
+ * @library ../helpers
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageCreateImageRuntimeBase
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageCreateImageRuntimeTest
+ */
+public class JPackageCreateImageRuntimeTest {
+    private static final String [] CMD = {
+        "create-image",
+        "--runtime-image", "runtime",
+        "--input", "input",
+        "--output", "output",
+        "--name", "test",
+        "--main-jar", "hello.jar",
+        "--main-class", "Hello",
+        "--overwrite",
+        "--files", "hello.jar"};
+
+    public static void main(String[] args) throws Exception {
+        JPackageHelper.createHelloImageJar();
+        JPackageHelper.createRuntime();
+        JPackageCreateImageRuntimeBase.testCreateImage(CMD);
+        JPackageCreateImageRuntimeBase.testCreateImageToolProvider(CMD);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createimage/JPackageCreateImageSecondaryLauncherBase.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.PrintWriter;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.List;
+
+public class JPackageCreateImageSecondaryLauncherBase {
+    private static final String app = JPackagePath.getApp();
+    private static final String app2 = JPackagePath.getAppSL();
+    private static final String appOutput = JPackagePath.getAppOutputFile();
+    private static final String appWorkingDir = JPackagePath.getAppWorkingDir();
+
+    // Note: quotes in argument for secondary launcher is not support by test
+    private static final String ARGUMENT1 = "argument 1";
+    private static final String ARGUMENT2 = "argument 2";
+    private static final String ARGUMENT3 = "argument 3";
+
+    private static final List<String> arguments = new ArrayList<>();
+
+    private static final String PARAM1 = "-Dparam1=Some Param 1";
+    private static final String PARAM2 = "-Dparam2=Some Param 2";
+    private static final String PARAM3 = "-Dparam3=Some Param 3";
+
+    private static final List<String> vmArguments = new ArrayList<>();
+
+    private static void validateResult(List<String> args, List<String> vmArgs)
+            throws Exception {
+        File outfile = new File(appWorkingDir + File.separator + appOutput);
+        if (!outfile.exists()) {
+            throw new AssertionError(appOutput + " was not created");
+        }
+
+        String output = Files.readString(outfile.toPath());
+        String[] result = output.split("\n");
+
+        if (result.length != (args.size() + vmArgs.size() + 2)) {
+            throw new AssertionError("Unexpected number of lines: "
+                    + result.length);
+        }
+
+        if (!result[0].trim().equals("jpackage test application")) {
+            throw new AssertionError("Unexpected result[0]: " + result[0]);
+        }
+
+        if (!result[1].trim().equals("args.length: " + args.size())) {
+            throw new AssertionError("Unexpected result[1]: " + result[1]);
+        }
+
+        int index = 2;
+        for (String arg : args) {
+            if (!result[index].trim().equals(arg)) {
+                throw new AssertionError("Unexpected result[" + index + "]: "
+                    + result[index]);
+            }
+            index++;
+        }
+
+        for (String vmArg : vmArgs) {
+            if (!result[index].trim().equals(vmArg)) {
+                throw new AssertionError("Unexpected result[" + index + "]: "
+                    + result[index]);
+            }
+            index++;
+        }
+    }
+
+    private static void validate() throws Exception {
+        int retVal = JPackageHelper.execute(null, app);
+        if (retVal != 0) {
+            throw new AssertionError("Test application exited with error: "
+                    + retVal);
+        }
+        validateResult(new ArrayList<>(), new ArrayList<>());
+
+        retVal = JPackageHelper.execute(null, app2);
+        if (retVal != 0) {
+            throw new AssertionError("Test application exited with error: "
+                    + retVal);
+        }
+        validateResult(arguments, vmArguments);
+    }
+
+    public static void testCreateImage(String [] cmd) throws Exception {
+        JPackageHelper.executeCLI(true, cmd);
+        validate();
+    }
+
+    public static void testCreateImageToolProvider(String [] cmd) throws Exception {
+        JPackageHelper.executeToolProvider(true, cmd);
+        validate();
+    }
+
+    public static void createSLProperties() throws Exception {
+        arguments.add(ARGUMENT1);
+        arguments.add(ARGUMENT2);
+        arguments.add(ARGUMENT3);
+
+        String argumentsMap =
+                JPackageHelper.listToArgumentsMap(arguments, true);
+
+        vmArguments.add(PARAM1);
+        vmArguments.add(PARAM2);
+        vmArguments.add(PARAM3);
+
+        String vmArgumentsMap =
+                JPackageHelper.listToArgumentsMap(vmArguments, true);
+
+        try (PrintWriter out = new PrintWriter(new BufferedWriter(
+                new FileWriter("sl.properties")))) {
+            out.println("name=test2");
+            out.println("arguments=" + argumentsMap);
+            out.println("jvm-args=" + vmArgumentsMap);
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createimage/JPackageCreateImageSecondaryLauncherModuleTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+ /*
+ * @test
+ * @summary jpackage create image with secondary launcher test
+ * @library ../helpers
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageCreateImageSecondaryLauncherBase
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageCreateImageSecondaryLauncherModuleTest
+ */
+public class JPackageCreateImageSecondaryLauncherModuleTest {
+    private static final String [] CMD = {
+        "create-image",
+        "--output", "output",
+        "--name", "test",
+        "--overwrite",
+        "--module", "com.hello/com.hello.Hello",
+        "--module-path", "input",
+        "--secondary-launcher", "sl.properties"};
+
+    public static void main(String[] args) throws Exception {
+        JPackageHelper.createHelloModule();
+        JPackageCreateImageSecondaryLauncherBase.createSLProperties();
+        JPackageCreateImageSecondaryLauncherBase.testCreateImage(CMD);
+        JPackageCreateImageSecondaryLauncherBase.testCreateImageToolProvider(CMD);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createimage/JPackageCreateImageSecondaryLauncherTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+ /*
+ * @test
+ * @summary jpackage create image with secondary launcher test
+ * @library ../helpers
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageCreateImageSecondaryLauncherBase
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageCreateImageSecondaryLauncherTest
+ */
+public class JPackageCreateImageSecondaryLauncherTest {
+    private static final String [] CMD = {
+        "create-image",
+        "--input", "input",
+        "--output", "output",
+        "--name", "test",
+        "--main-jar", "hello.jar",
+        "--main-class", "Hello",
+        "--files", "hello.jar",
+        "--overwrite",
+        "--secondary-launcher", "sl.properties"};
+
+    public static void main(String[] args) throws Exception {
+        JPackageHelper.createHelloImageJar();
+        JPackageCreateImageSecondaryLauncherBase.createSLProperties();
+        JPackageCreateImageSecondaryLauncherBase.testCreateImage(CMD);
+        JPackageCreateImageSecondaryLauncherBase.testCreateImageToolProvider(CMD);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createimage/JPackageCreateImageStripNativeCommandsTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.File;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import java.util.stream.Collectors;
+
+ /*
+ * @test
+ * @summary jpackage create image with --strip-native-commands test
+ * @library ../helpers
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageCreateImageStripNativeCommandsTest
+ */
+public class JPackageCreateImageStripNativeCommandsTest {
+    private static final String runtimeBinPath = JPackagePath.getRuntimeBin();
+
+    private static final String [] CMD = {
+        "create-image",
+        "--input", "input",
+        "--output", "output",
+        "--name", "test",
+        "--main-jar", "hello.jar",
+        "--main-class", "Hello",
+        "--files", "hello.jar",
+        "--overwrite",
+        "--strip-native-commands"};
+
+    private static void validate() throws Exception {
+        if (JPackageHelper.isWindows()) {
+            Path binPath = Paths.get(runtimeBinPath).toAbsolutePath();
+            List<Path> files = Files.walk(binPath).collect(Collectors.toList());
+            files.forEach((f) -> {
+                if (f.toString().endsWith(".exe")) {
+                    throw new AssertionError(
+                            "Found executable file in runtime bin folder: "
+                            + f.toString());
+                }
+            });
+        } else {
+            File binFolder = new File(runtimeBinPath);
+            if (binFolder.exists()) {
+                throw new AssertionError("Found bin folder in runtime: "
+                            + binFolder.toString());
+            }
+        }
+    }
+
+    private static void testCreateImage() throws Exception {
+        JPackageHelper.executeCLI(true, CMD);
+        validate();
+    }
+
+    private static void testCreateImageToolProvider() throws Exception {
+        JPackageHelper.executeToolProvider(true, CMD);
+        validate();
+    }
+
+    public static void main(String[] args) throws Exception {
+        JPackageHelper.createHelloImageJar();
+        testCreateImage();
+        testCreateImageToolProvider();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createimage/JPackageCreateImageTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+ /*
+ * @test
+ * @summary jpackage create image test
+ * @library ../helpers
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageCreateImageBase
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageCreateImageTest
+ */
+public class JPackageCreateImageTest {
+    private static final String [] CMD = {
+        "create-image",
+        "--input", "input",
+        "--output", "output",
+        "--name", "test",
+        "--main-jar", "hello.jar",
+        "--main-class", "Hello",
+        "--overwrite",
+        "--files", "hello.jar"};
+
+    public static void main(String[] args) throws Exception {
+        JPackageHelper.createHelloImageJar();
+        JPackageCreateImageBase.testCreateImage(CMD);
+        JPackageCreateImageBase.testCreateImageToolProvider(CMD);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createimage/JPackageCreateImageVerboseTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary jpackage create image verbose test
+ * @library ../helpers
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageCreateImageVerboseTest
+ */
+public class JPackageCreateImageVerboseTest {
+
+    private static final String[] CMD = {
+        "create-image",
+        "--input", "input",
+        "--output", "output",
+        "--name", "test",
+        "--main-jar", "hello.jar",
+        "--main-class", "Hello",
+        "--overwrite",
+        "--files", "hello.jar"};
+
+    private static final String[] CMD_VERBOSE = {
+        "create-image",
+        "--input", "input",
+        "--output", "output",
+        "--name", "test",
+        "--main-jar", "hello.jar",
+        "--main-class", "Hello",
+        "--files", "hello.jar",
+        "--overwrite",
+        "--verbose"};
+
+    private static void validate(String result, String resultVerbose)
+            throws Exception {
+        String[] r = result.split("\n");
+        String[] rv = resultVerbose.split("\n");
+
+        if (r.length >= rv.length) {
+            System.err.println("r.length: " + r.length);
+            System.err.println(result);
+            System.err.println("rv.length: " + rv.length);
+            System.err.println(resultVerbose);
+            throw new AssertionError(
+                    "non-verbose output is less or equal to verbose output");
+        }
+    }
+
+    private static void testCreateImage() throws Exception {
+        String result = JPackageHelper.executeCLI(true, CMD);
+        String resultVerbose = JPackageHelper.executeCLI(true, CMD_VERBOSE);
+        validate(result, resultVerbose);
+    }
+
+    private static void testCreateImageToolProvider() throws Exception {
+        String result = JPackageHelper.executeToolProvider(true, CMD);
+        String resultVerbose =
+                JPackageHelper.executeToolProvider(true, CMD_VERBOSE);
+        validate(result, resultVerbose);
+    }
+
+    public static void main(String[] args) throws Exception {
+        JPackageHelper.createHelloImageJar();
+        testCreateImage();
+        testCreateImageToolProvider();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createimage/JPackageCreateImageVersionTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,102 @@
+
+import java.io.File;
+import java.nio.file.Files;
+
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary jpackage create image --app-version test
+ * @library ../helpers
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageCreateImageVersionTest
+ */
+public class JPackageCreateImageVersionTest {
+    private static final String appCfg = JPackagePath.getAppCfg();
+    private static final String VERSION = "2.3";
+    private static final String VERSION_DEFAULT = "1.0";
+
+    private static final String[] CMD = {
+        "create-image",
+        "--input", "input",
+        "--output", "output",
+        "--name", "test",
+        "--main-jar", "hello.jar",
+        "--main-class", "Hello",
+        "--overwrite",
+        "--files", "hello.jar"};
+
+    private static final String[] CMD_VERSION = {
+        "create-image",
+        "--input", "input",
+        "--output", "output",
+        "--name", "test",
+        "--main-jar", "hello.jar",
+        "--main-class", "Hello",
+        "--files", "hello.jar",
+        "--overwrite",
+        "--app-version", VERSION};
+
+    private static void validate(String version)
+            throws Exception {
+        File outfile = new File(appCfg);
+        if (!outfile.exists()) {
+            throw new AssertionError(appCfg + " was not created");
+        }
+
+        String output = Files.readString(outfile.toPath());
+        if (version == null) {
+            version = VERSION_DEFAULT;
+        }
+
+        String expected = "app.version=" + version;
+        if (!output.contains(expected)) {
+            System.err.println("Expected: " + expected);
+            throw new AssertionError("Cannot find expected entry in config file");
+        }
+    }
+
+    private static void testVersion() throws Exception {
+        JPackageHelper.executeCLI(true, CMD);
+        validate(null);
+        JPackageHelper.executeCLI(true, CMD_VERSION);
+        validate(VERSION);
+    }
+
+    private static void testVersionToolProvider() throws Exception {
+        JPackageHelper.executeToolProvider(true, CMD);
+        validate(null);
+        JPackageHelper.executeToolProvider(true, CMD_VERSION);
+        validate(VERSION);
+    }
+
+    public static void main(String[] args) throws Exception {
+        JPackageHelper.createHelloImageJar();
+        testVersion();
+        testVersionToolProvider();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/linux/base/JPackageCreateInstallerBase.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+public class JPackageCreateInstallerBase {
+
+    private static String TEST_NAME;
+    private static String EXT;
+    private static String OUTPUT;
+    private static String[] CMD;
+
+    private static void copyResults() throws Exception {
+        List<String> files = new ArrayList<>();
+        files.add(OUTPUT.toLowerCase());
+        JPackageInstallerHelper.copyTestResults(files);
+    }
+
+    private static void testCreateInstaller() throws Exception {
+        JPackageHelper.executeCLI(true, CMD);
+        JPackageInstallerHelper.validateOutput(OUTPUT);
+        copyResults();
+    }
+
+    private static void verifyInstall() throws Exception {
+        String app = JPackagePath.getLinuxInstalledApp(TEST_NAME);
+        JPackageInstallerHelper.validateApp(app);
+    }
+
+    private static void verifyUnInstall() throws Exception {
+        String folderPath = JPackagePath.getLinuxInstallFolder(TEST_NAME);
+        File folder = new File(folderPath);
+        if (folder.exists()) {
+            throw new AssertionError("Error: " + folder.getAbsolutePath() + " exist");
+        }
+    }
+
+    private static void init(String name, String ext) {
+        TEST_NAME = name;
+        EXT = ext;
+        if (EXT.equals("rpm")) {
+            OUTPUT = "output" + File.separator + TEST_NAME + "-1.0-1.x86_64." + EXT;
+        } else {
+            OUTPUT = "output" + File.separator + TEST_NAME + "-1.0." + EXT;
+        }
+        CMD = new String[]{
+            "create-installer",
+            "--installer-type", EXT,
+            "--input", "input",
+            "--output", "output",
+            "--name", TEST_NAME,
+            "--main-jar", "hello.jar",
+            "--main-class", "Hello",
+            "--overwrite",
+            "--files", "hello.jar"};
+    }
+
+    public static void run(String name, String ext) throws Exception {
+        init(name, ext);
+
+        if (JPackageInstallerHelper.isVerifyInstall()) {
+            verifyInstall();
+        } else if (JPackageInstallerHelper.isVerifyUnInstall()) {
+            verifyUnInstall();
+        } else {
+            JPackageHelper.createHelloInstallerJar();
+            testCreateInstaller();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/linux/base/JPackageCreateInstallerBundleNameBase.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+public class JPackageCreateInstallerBundleNameBase {
+
+    private static String TEST_NAME;
+    private static String BUNDLE_NAME;
+    private static String EXT;
+    private static String OUTPUT;
+    private static String[] CMD;
+
+    private static void copyResults() throws Exception {
+        List<String> files = new ArrayList<>();
+        files.add(OUTPUT.toLowerCase());
+        JPackageInstallerHelper.copyTestResults(files);
+    }
+
+    private static void testCreateInstaller() throws Exception {
+        JPackageHelper.executeCLI(true, CMD);
+        JPackageInstallerHelper.validateOutput(OUTPUT);
+        copyResults();
+    }
+
+    private static void verifyInstall() throws Exception {
+        String app = JPackagePath.getLinuxInstalledApp(TEST_NAME);
+        JPackageInstallerHelper.validateApp(app);
+    }
+
+    private static void verifyUnInstall() throws Exception {
+        String folderPath = JPackagePath.getLinuxInstallFolder(TEST_NAME);
+        File folder = new File(folderPath);
+        if (folder.exists()) {
+            throw new AssertionError("Error: " + folder.getAbsolutePath() + " exist");
+        }
+    }
+
+    private static void init(String name, String ext) {
+        TEST_NAME = name;
+        BUNDLE_NAME = "jpackage-test-bundle-name";
+        EXT = ext;
+        if (EXT.equals("rpm")) {
+            OUTPUT = "output" + File.separator + BUNDLE_NAME + "-1.0-1.x86_64." + EXT;
+        } else {
+            OUTPUT = "output" + File.separator + BUNDLE_NAME + "-1.0." + EXT;
+        }
+        CMD = new String[]{
+            "create-installer",
+            "--installer-type", EXT,
+            "--input", "input",
+            "--output", "output",
+            "--name", TEST_NAME,
+            "--main-jar", "hello.jar",
+            "--main-class", "Hello",
+            "--overwrite",
+            "--files", "hello.jar",
+            "--linux-bundle-name", BUNDLE_NAME};
+    }
+
+    public static void run(String name, String ext) throws Exception {
+        init(name, ext);
+
+        if (JPackageInstallerHelper.isVerifyInstall()) {
+            verifyInstall();
+        } else if (JPackageInstallerHelper.isVerifyUnInstall()) {
+            verifyUnInstall();
+        } else {
+            JPackageHelper.createHelloInstallerJar();
+            testCreateInstaller();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/linux/base/JPackageCreateInstallerFileAssociationsBase.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.awt.Desktop;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.PrintWriter;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.List;
+
+public class JPackageCreateInstallerFileAssociationsBase {
+
+    private static String TEST_NAME;
+    private static String EXT;
+    private static String TEST_EXT;
+    private static String OUTPUT;
+    private static String[] CMD;
+
+    private static void copyResults() throws Exception {
+        List<String> files = new ArrayList<>();
+        files.add(OUTPUT.toLowerCase());
+        JPackageInstallerHelper.copyTestResults(files);
+    }
+
+    private static void testCreateInstaller() throws Exception {
+        JPackageHelper.executeCLI(true, CMD);
+        JPackageInstallerHelper.validateOutput(OUTPUT);
+        copyResults();
+    }
+
+    private static void validateAppOutput() throws Exception {
+        File outFile = new File("appOutput.txt");
+        int count = 10;
+        boolean bOutputCreated = false;
+        while (count > 0) {
+            if (!outFile.exists()) {
+                Thread.sleep(3000);
+                count--;
+            } else {
+                bOutputCreated = true;
+                break;
+            }
+        }
+
+        if (!bOutputCreated) {
+            throw new AssertionError(outFile.getAbsolutePath() + " was not created");
+        }
+
+        String output = Files.readString(outFile.toPath());
+        String[] result = output.split("\n");
+        if (result.length != 3) {
+            System.err.println(output);
+            throw new AssertionError(
+                    "Unexpected number of lines: " + result.length);
+        }
+
+        if (!result[0].trim().equals("jpackage test application")) {
+            throw new AssertionError("Unexpected result[0]: " + result[0]);
+        }
+
+        if (!result[1].trim().equals("args.length: 1")) {
+            throw new AssertionError("Unexpected result[1]: " + result[1]);
+        }
+
+        File faFile = new File(TEST_NAME + "." + TEST_EXT);
+        if (!result[2].trim().equals(faFile.getAbsolutePath())) {
+            throw new AssertionError("Unexpected result[2]: " + result[2]);
+        }
+    }
+
+    private static void verifyInstall() throws Exception {
+        createFileAssociationsTestFile();
+        Desktop.getDesktop().open(new File(TEST_NAME + "." + TEST_EXT));
+        validateAppOutput();
+    }
+
+    private static void verifyUnInstall() throws Exception {
+        String folderPath = JPackagePath.getLinuxInstallFolder(TEST_NAME);
+        File folder = new File(folderPath);
+        if (folder.exists()) {
+            throw new AssertionError("Error: " + folder.getAbsolutePath() + " exist");
+        }
+    }
+
+    private static void createFileAssociationsTestFile() throws Exception {
+        try (PrintWriter out = new PrintWriter(new BufferedWriter(
+                new FileWriter(TEST_NAME + "." + TEST_EXT)))) {
+            out.println(TEST_NAME);
+        }
+    }
+
+    private static void createFileAssociationsProperties() throws Exception {
+        try (PrintWriter out = new PrintWriter(new BufferedWriter(
+                new FileWriter("fa.properties")))) {
+            out.println("extension=" + TEST_EXT);
+            out.println("mime-type=application/" + TEST_EXT);
+            out.println("description=jpackage test extention");
+        }
+    }
+
+    private static void init(String name, String ext) {
+        TEST_NAME = name;
+        EXT = ext;
+        TEST_EXT = "jptest1";
+        if (EXT.equals("rpm")) {
+            OUTPUT = "output" + File.separator + TEST_NAME + "-1.0-1.x86_64." + EXT;
+        } else {
+            OUTPUT = "output" + File.separator + TEST_NAME + "-1.0." + EXT;
+        }
+        CMD = new String[]{
+            "create-installer",
+            "--installer-type", EXT,
+            "--input", "input",
+            "--output", "output",
+            "--name", TEST_NAME,
+            "--main-jar", "hello.jar",
+            "--main-class", "Hello",
+            "--overwrite",
+            "--files", "hello.jar",
+            "--file-associations", "fa.properties"};
+    }
+
+    public static void run(String name, String ext) throws Exception {
+        init(name, ext);
+
+        if (JPackageInstallerHelper.isVerifyInstall()) {
+            verifyInstall();
+        } else if (JPackageInstallerHelper.isVerifyUnInstall()) {
+            verifyUnInstall();
+        } else {
+            JPackageHelper.createHelloInstallerJar();
+            createFileAssociationsProperties();
+            testCreateInstaller();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/linux/base/JPackageCreateInstallerInstallDirBase.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+public class JPackageCreateInstallerInstallDirBase {
+
+    private static String TEST_NAME;
+    private static String EXT;
+    private static String OUTPUT;
+    private static String[] CMD;
+
+    private static void copyResults() throws Exception {
+        List<String> files = new ArrayList<>();
+        files.add(OUTPUT.toLowerCase());
+        JPackageInstallerHelper.copyTestResults(files);
+    }
+
+    private static void testCreateInstaller() throws Exception {
+        JPackageHelper.executeCLI(true, CMD);
+        JPackageInstallerHelper.validateOutput(OUTPUT);
+        copyResults();
+    }
+
+    private static void verifyInstall() throws Exception {
+        String app = JPackagePath.getLinuxInstalledApp("jpackage", TEST_NAME);
+        JPackageInstallerHelper.validateApp(app);
+    }
+
+    private static void verifyUnInstall() throws Exception {
+        String folderPath = JPackagePath.getLinuxInstallFolder("jpackage", TEST_NAME);
+        File folder = new File(folderPath);
+        if (folder.exists()) {
+            throw new AssertionError("Error: " + folder.getAbsolutePath() + " exist");
+        }
+
+        folderPath = JPackagePath.getLinuxInstallFolder("jpackage", null);
+        folder = new File(folderPath);
+        if (folder.exists()) {
+            throw new AssertionError("Error: " + folder.getAbsolutePath() + " exist");
+        }
+    }
+
+    private static void init(String name, String ext) {
+        TEST_NAME = name;
+        EXT = ext;
+        if (EXT.equals("rpm")) {
+            OUTPUT = "output" + File.separator + TEST_NAME + "-1.0-1.x86_64." + EXT;
+        } else {
+            OUTPUT = "output" + File.separator + TEST_NAME + "-1.0." + EXT;
+        }
+        CMD = new String[]{
+            "create-installer",
+            "--installer-type", EXT,
+            "--input", "input",
+            "--output", "output",
+            "--name", TEST_NAME,
+            "--main-jar", "hello.jar",
+            "--main-class", "Hello",
+            "--overwrite",
+            "--files", "hello.jar",
+            "--install-dir", "/opt/jpackage"};
+    }
+
+    public static void run(String name, String ext) throws Exception {
+        init(name, ext);
+
+        if (JPackageInstallerHelper.isVerifyInstall()) {
+            verifyInstall();
+        } else if (JPackageInstallerHelper.isVerifyUnInstall()) {
+            verifyUnInstall();
+        } else {
+            JPackageHelper.createHelloInstallerJar();
+            testCreateInstaller();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/linux/base/JPackageCreateInstallerLicenseBase.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+public class JPackageCreateInstallerLicenseBase {
+
+    private static String TEST_NAME;
+    private static String EXT;
+    private static String OUTPUT;
+    private static String[] CMD;
+
+    private static void copyResults() throws Exception {
+        List<String> files = new ArrayList<>();
+        files.add(OUTPUT.toLowerCase());
+        JPackageInstallerHelper.copyTestResults(files);
+    }
+
+    private static void testCreateInstaller() throws Exception {
+        JPackageHelper.executeCLI(true, CMD);
+        JPackageInstallerHelper.validateOutput(OUTPUT);
+        copyResults();
+    }
+
+    private static void verifyInstall() throws Exception {
+        String app = JPackagePath.getLinuxInstalledApp(TEST_NAME);
+        JPackageInstallerHelper.validateApp(app);
+
+    }
+
+    private static void verifyUnInstall() throws Exception {
+        String folderPath = JPackagePath.getLinuxInstallFolder(TEST_NAME);
+        File folder = new File(folderPath);
+        if (folder.exists()) {
+            throw new AssertionError("Error: " + folder.getAbsolutePath() + " exist");
+        }
+    }
+
+    private static void init(String name, String ext) {
+        TEST_NAME = name;
+        EXT = ext;
+        if (EXT.equals("rpm")) {
+            OUTPUT = "output" + File.separator + TEST_NAME + "-1.0-1.x86_64." + EXT;
+        } else {
+            OUTPUT = "output" + File.separator + TEST_NAME + "-1.0." + EXT;
+        }
+        CMD = new String [] {
+        "create-installer",
+        "--installer-type", EXT,
+        "--input", "input",
+        "--output", "output",
+        "--name", TEST_NAME,
+        "--main-jar", "hello.jar",
+        "--main-class", "Hello",
+        "--overwrite",
+        "--files", "hello.jar",
+        "--license-file", JPackagePath.getLicenseFilePath()};
+    }
+
+    public static void run(String name, String ext) throws Exception {
+        init(name, ext);
+
+        if (JPackageInstallerHelper.isVerifyInstall()) {
+            verifyInstall();
+        } else if (JPackageInstallerHelper.isVerifyUnInstall()) {
+            verifyUnInstall();
+        } else {
+            JPackageHelper.createHelloInstallerJar();
+            testCreateInstaller();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/linux/base/JPackageCreateInstallerLicenseTypeBase.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.File;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.List;
+
+public class JPackageCreateInstallerLicenseTypeBase {
+
+    private static String TEST_NAME;
+    private static String EXT;
+    private static String JP_LICENSE_TYPE;
+    private static String OUTPUT;
+    private static String[] CMD;
+
+    private static void copyResults() throws Exception {
+        List<String> files = new ArrayList<>();
+        files.add(OUTPUT.toLowerCase());
+        JPackageInstallerHelper.copyTestResults(files);
+    }
+
+    private static final String infoResult = "infoResult.txt";
+    private static void validatePackage() throws Exception {
+        int retVal = JPackageHelper.execute(new File(infoResult),"rpm",
+                "--query", "--package", "--info", OUTPUT.toLowerCase());
+        if (retVal != 0) {
+            throw new AssertionError("rpm exited with error: " + retVal);
+        }
+
+        File outfile = new File(infoResult);
+        if (!outfile.exists()) {
+            throw new AssertionError(infoResult + " was not created");
+        }
+
+        String output = Files.readString(outfile.toPath());
+        if (!output.contains(JP_LICENSE_TYPE)) {
+            throw new AssertionError("Unexpected result: " + output);
+        }
+    }
+
+    private static void testCreateInstaller() throws Exception {
+        JPackageHelper.executeCLI(true, CMD);
+        JPackageInstallerHelper.validateOutput(OUTPUT);
+        validatePackage();
+        copyResults();
+    }
+
+    private static void verifyInstall() throws Exception {
+        String app = JPackagePath.getLinuxInstalledApp(TEST_NAME);
+        JPackageInstallerHelper.validateApp(app);
+    }
+
+    private static void verifyUnInstall() throws Exception {
+        String folderPath = JPackagePath.getLinuxInstallFolder(TEST_NAME);
+        File folder = new File(folderPath);
+        if (folder.exists()) {
+            throw new AssertionError("Error: " + folder.getAbsolutePath() + " exist");
+        }
+    }
+
+    private static void init(String name, String ext) {
+        TEST_NAME = name;
+        EXT = ext;
+        JP_LICENSE_TYPE = "JP_LICENSE_TYPE";
+        if (EXT.equals("rpm")) {
+            OUTPUT = "output" + File.separator + TEST_NAME + "-1.0-1.x86_64." + EXT;
+        } else {
+            OUTPUT = "output" + File.separator + TEST_NAME + "-1.0." + EXT;
+        }
+        CMD = new String[]{
+            "create-installer",
+            "--installer-type", EXT,
+            "--input", "input",
+            "--output", "output",
+            "--name", TEST_NAME,
+            "--main-jar", "hello.jar",
+            "--main-class", "Hello",
+            "--overwrite",
+            "--files", "hello.jar",
+            "--linux-rpm-license-type", JP_LICENSE_TYPE};
+    }
+
+    public static void run(String name, String ext) throws Exception {
+        init(name, ext);
+
+        if (JPackageInstallerHelper.isVerifyInstall()) {
+            verifyInstall();
+        } else if (JPackageInstallerHelper.isVerifyUnInstall()) {
+            verifyUnInstall();
+        } else {
+            JPackageHelper.createHelloInstallerJar();
+            testCreateInstaller();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/linux/base/JPackageCreateInstallerMaintainerBase.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.File;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.List;
+
+public class JPackageCreateInstallerMaintainerBase {
+
+    private static String TEST_NAME;
+    private static String EMAIL;
+    private static String EXT;
+    private static String OUTPUT;
+    private static String[] CMD;
+
+    private static void copyResults() throws Exception {
+        List<String> files = new ArrayList<>();
+        files.add(OUTPUT.toLowerCase());
+        JPackageInstallerHelper.copyTestResults(files);
+    }
+
+    private static final String infoResult = "infoResult.txt";
+    private static void validatePackage() throws Exception {
+        int retVal = JPackageHelper.execute(new File(infoResult), "dpkg",
+                "--info", OUTPUT.toLowerCase());
+        if (retVal != 0) {
+            throw new AssertionError("dpkg exited with error: " + retVal);
+        }
+
+        File outfile = new File(infoResult);
+        if (!outfile.exists()) {
+            throw new AssertionError(infoResult + " was not created");
+        }
+
+        String output = Files.readString(outfile.toPath());
+        if (!output.contains("Maintainer: " + EMAIL)) {
+            throw new AssertionError("Unexpected result: " + output);
+        }
+    }
+
+    private static void testCreateInstaller() throws Exception {
+        JPackageHelper.executeCLI(true, CMD);
+        JPackageInstallerHelper.validateOutput(OUTPUT);
+        validatePackage();
+        copyResults();
+    }
+
+    private static void verifyInstall() throws Exception {
+        String app = JPackagePath.getLinuxInstalledApp(TEST_NAME);
+        JPackageInstallerHelper.validateApp(app);
+    }
+
+    private static void verifyUnInstall() throws Exception {
+        String folderPath = JPackagePath.getLinuxInstallFolder(TEST_NAME);
+        File folder = new File(folderPath);
+        if (folder.exists()) {
+            throw new AssertionError("Error: " + folder.getAbsolutePath() + " exist");
+        }
+    }
+
+    private static void init(String name, String ext) {
+        TEST_NAME = name;
+        EMAIL = "jpackage-test@java.com";
+        EXT = ext;
+        OUTPUT = "output" + File.separator + TEST_NAME + "-1.0." + EXT;
+        CMD = new String[]{
+            "create-installer",
+            EXT,
+            "--input", "input",
+            "--output", "output",
+            "--name", TEST_NAME,
+            "--main-jar", "hello.jar",
+            "--main-class", "Hello",
+            "--overwrite",
+            "--files", "hello.jar",
+            "--linux-deb-maintainer", EMAIL};
+    }
+
+    public static void run(String name, String ext) throws Exception {
+        init(name, ext);
+
+        if (JPackageInstallerHelper.isVerifyInstall()) {
+            verifyInstall();
+        } else if (JPackageInstallerHelper.isVerifyUnInstall()) {
+            verifyUnInstall();
+        } else {
+            JPackageHelper.createHelloInstallerJar();
+            testCreateInstaller();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/linux/base/JPackageCreateInstallerPackageDepsBase.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.File;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.List;
+
+public class JPackageCreateInstallerPackageDepsBase {
+
+    private static String TEST_NAME;
+    private static String DEP_NAME;
+    private static String EXT;
+    private static String OUTPUT;
+    private static String OUTPUT_DEP;
+    private static String[] CMD;
+    private static String[] CMD_DEP;
+
+    private static void copyResults() throws Exception {
+        List<String> files = new ArrayList<>();
+        files.add(OUTPUT.toLowerCase());
+        files.add(OUTPUT_DEP.toLowerCase());
+        JPackageInstallerHelper.copyTestResults(files);
+    }
+
+    private static final String infoResult = "infoResult.txt";
+    private static void validatePackage() throws Exception {
+        if (EXT.equals("rpm")) {
+            int retVal = JPackageHelper.execute(new File(infoResult),"rpm",
+                    "--query", "--package", "--requires", OUTPUT.toLowerCase());
+            if (retVal != 0) {
+                throw new AssertionError("rpm exited with error: " + retVal);
+            }
+        } else {
+            int retVal = JPackageHelper.execute(new File(infoResult), "dpkg",
+                    "--info", OUTPUT.toLowerCase());
+            if (retVal != 0) {
+                throw new AssertionError("dpkg exited with error: " + retVal);
+            }
+        }
+
+        File outfile = new File(infoResult);
+        if (!outfile.exists()) {
+            throw new AssertionError(infoResult + " was not created");
+        }
+
+        String output = Files.readString(outfile.toPath());
+        if (!output.contains(DEP_NAME.toLowerCase())) {
+            throw new AssertionError("Unexpected result: " + output);
+        }
+    }
+
+    private static void testCreateInstaller() throws Exception {
+        JPackageHelper.executeCLI(true, CMD);
+        JPackageHelper.executeCLI(true, CMD_DEP);
+        JPackageInstallerHelper.validateOutput(OUTPUT);
+        JPackageInstallerHelper.validateOutput(OUTPUT_DEP);
+        validatePackage();
+        copyResults();
+    }
+
+    private static void verifyInstall() throws Exception {
+        String app = JPackagePath.getLinuxInstalledApp(TEST_NAME);
+        JPackageInstallerHelper.validateApp(app);
+
+        app = JPackagePath.getLinuxInstalledApp(DEP_NAME);
+        JPackageInstallerHelper.validateApp(app);
+    }
+
+    private static void verifyUnInstall() throws Exception {
+        String folderPath = JPackagePath.getLinuxInstallFolder(TEST_NAME);
+        File folder = new File(folderPath);
+        if (folder.exists()) {
+            throw new AssertionError("Error: " + folder.getAbsolutePath() + " exist");
+        }
+
+        folderPath = JPackagePath.getLinuxInstallFolder(DEP_NAME);
+        folder = new File(folderPath);
+        if (folder.exists()) {
+            throw new AssertionError("Error: " + folder.getAbsolutePath() + " exist");
+        }
+    }
+
+    private static void init(String name, String ext) {
+        TEST_NAME = name;
+        DEP_NAME = name + "Dep";
+        EXT = ext;
+        if (EXT.equals("rpm")) {
+            OUTPUT = "output" + File.separator + TEST_NAME + "-1.0-1.x86_64." + EXT;
+            OUTPUT_DEP = "output" + File.separator + DEP_NAME + "-1.0-1.x86_64." + EXT;
+        } else {
+            OUTPUT = "output" + File.separator + TEST_NAME + "-1.0." + EXT;
+            OUTPUT_DEP = "output" + File.separator + DEP_NAME + "-1.0." + EXT;
+        }
+        CMD = new String[]{
+            "create-installer",
+            "--installer-type", EXT,
+            "--input", "input",
+            "--output", "output",
+            "--name", TEST_NAME,
+            "--main-jar", "hello.jar",
+            "--main-class", "Hello",
+            "--overwrite",
+            "--files", "hello.jar",
+            "--linux-package-deps", DEP_NAME.toLowerCase()};
+        CMD_DEP = new String[]{
+            "create-installer",
+            "--installer-type", EXT,
+            "--input", "input",
+            "--output", "output",
+            "--name", DEP_NAME,
+            "--main-jar", "hello.jar",
+            "--main-class", "Hello",
+            "--overwrite",
+            "--files", "hello.jar"};
+    }
+
+    public static void run(String name, String ext) throws Exception {
+        init(name, ext);
+
+        if (JPackageInstallerHelper.isVerifyInstall()) {
+            verifyInstall();
+        } else if (JPackageInstallerHelper.isVerifyUnInstall()) {
+            verifyUnInstall();
+        } else {
+            JPackageHelper.createHelloInstallerJar();
+            testCreateInstaller();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/linux/deb/JPackageCreateInstallerBundleNameTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerBundleNameBase
+ * @requires (os.family == "linux")
+ * @modules jdk.jpackage
+ * @ignore
+ * @run main/othervm -Xmx512m JPackageCreateInstallerBundleNameTest
+ */
+public class JPackageCreateInstallerBundleNameTest {
+    private static final String TEST_NAME = "JPackageCreateInstallerBundleNameTest";
+    private static final String EXT = "deb";
+
+    public static void main(String[] args) throws Exception {
+        JPackageCreateInstallerBundleNameBase.run(TEST_NAME, EXT);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/linux/deb/JPackageCreateInstallerFileAssociationsTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerFileAssociationsBase
+ * @requires (os.family == "linux")
+ * @modules jdk.jpackage
+ * @ignore
+ * @run main/othervm -Xmx512m JPackageCreateInstallerFileAssociationsTest
+ */
+public class JPackageCreateInstallerFileAssociationsTest {
+    private static final String TEST_NAME = "JPackageCreateInstallerFileAssociationsTest";
+    private static final String EXT = "deb";
+
+    public static void main(String[] args) throws Exception {
+        JPackageCreateInstallerFileAssociationsBase.run(TEST_NAME, EXT);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/linux/deb/JPackageCreateInstallerInstallDirTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerInstallDirBase
+ * @requires (os.family == "linux")
+ * @modules jdk.jpackage
+ * @ignore
+ * @run main/othervm -Xmx512m JPackageCreateInstallerInstallDirTest
+ */
+public class JPackageCreateInstallerInstallDirTest {
+    private static final String TEST_NAME = "JPackageCreateInstallerInstallDirTest";
+    private static final String EXT = "deb";
+
+    public static void main(String[] args) throws Exception {
+        JPackageCreateInstallerInstallDirBase.run(TEST_NAME, EXT);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/linux/deb/JPackageCreateInstallerLicenseTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerLicenseBase
+ * @requires (os.family == "linux")
+ * @modules jdk.jpackage
+ * @ignore
+ * @run main/othervm -Xmx512m JPackageCreateInstallerLicenseTest
+ */
+public class JPackageCreateInstallerLicenseTest {
+    private static final String TEST_NAME = "JPackageCreateInstallerLicenseTest";
+    private static final String EXT = "deb";
+
+    public static void main(String[] args) throws Exception {
+        JPackageCreateInstallerLicenseBase.run(TEST_NAME, EXT);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/linux/deb/JPackageCreateInstallerMaintainerTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerMaintainerBase
+ * @requires (os.family == "linux")
+ * @modules jdk.jpackage
+ * @ignore
+ * @run main/othervm -Xmx512m JPackageCreateInstallerMaintainerTest
+ */
+public class JPackageCreateInstallerMaintainerTest {
+    private static final String TEST_NAME = "JPackageCreateInstallerMaintainerTest";
+    private static final String EXT = "deb";
+
+    public static void main(String[] args) throws Exception {
+        JPackageCreateInstallerMaintainerBase.run(TEST_NAME, EXT);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/linux/deb/JPackageCreateInstallerPackageDepsTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerPackageDepsBase
+ * @requires (os.family == "linux")
+ * @modules jdk.jpackage
+ * @ignore
+ * @run main/othervm/timeout=240 -Xmx512m JPackageCreateInstallerPackageDepsTest
+ */
+public class JPackageCreateInstallerPackageDepsTest {
+    private static final String TEST_NAME = "JPackageCreateInstallerPackageDepsTest";
+    private static final String EXT = "deb";
+
+    public static void main(String[] args) throws Exception {
+        JPackageCreateInstallerPackageDepsBase.run(TEST_NAME, EXT);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/linux/deb/JPackageCreateInstallerTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerBase
+ * @requires (os.family == "linux")
+ * @modules jdk.jpackage
+ * @ignore
+ * @run main/othervm -Xmx512m JPackageCreateInstallerTest
+ */
+public class JPackageCreateInstallerTest {
+    private static final String TEST_NAME = "JPackageCreateInstallerTest";
+    private static final String EXT = "deb";
+
+    public static void main(String[] args) throws Exception {
+        JPackageCreateInstallerBase.run(TEST_NAME, EXT);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/linux/deb/install.sh	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,8 @@
+sudo dpkg -i jpackagecreateinstallertest-1.0.deb
+sudo dpkg -i jpackagecreateinstallerfileassociationstest-1.0.deb
+sudo dpkg -i jpackagecreateinstallerlicensetest-1.0.deb
+sudo dpkg -i jpackagecreateinstallerinstalldirtest-1.0.deb
+sudo dpkg -i jpackage-test-bundle-name-1.0.deb
+sudo dpkg -i jpackagecreateinstallermaintainertest-1.0.deb
+sudo dpkg -i jpackagecreateinstallerpackagedepstestdep-1.0.deb
+sudo dpkg -i jpackagecreateinstallerpackagedepstest-1.0.deb
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/linux/deb/uninstall.sh	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,8 @@
+sudo dpkg -r jpackagecreateinstallertest
+sudo dpkg -r jpackagecreateinstallerfileassociationstest
+sudo dpkg -r jpackagecreateinstallerlicensetest
+sudo dpkg -r jpackagecreateinstallerinstalldirtest
+sudo dpkg -r jpackage-test-bundle-name
+sudo dpkg -r jpackagecreateinstallermaintainertest
+sudo dpkg -r jpackagecreateinstallerpackagedepstest
+sudo dpkg -r jpackagecreateinstallerpackagedepstestdep
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/linux/rpm/JPackageCreateInstallerBundleNameTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerBundleNameBase
+ * @requires (os.family == "linux")
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageCreateInstallerBundleNameTest
+ */
+public class JPackageCreateInstallerBundleNameTest {
+    private static final String TEST_NAME = "JPackageCreateInstallerBundleNameTest";
+    private static final String EXT = "rpm";
+
+    public static void main(String[] args) throws Exception {
+        JPackageCreateInstallerBundleNameBase.run(TEST_NAME, EXT);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/linux/rpm/JPackageCreateInstallerFileAssociationsTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerFileAssociationsBase
+ * @requires (os.family == "linux")
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageCreateInstallerFileAssociationsTest
+ */
+public class JPackageCreateInstallerFileAssociationsTest {
+    private static final String TEST_NAME = "JPackageCreateInstallerFileAssociationsTest";
+    private static final String EXT = "rpm";
+
+    public static void main(String[] args) throws Exception {
+        JPackageCreateInstallerFileAssociationsBase.run(TEST_NAME, EXT);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/linux/rpm/JPackageCreateInstallerInstallDirTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerInstallDirBase
+ * @requires (os.family == "linux")
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageCreateInstallerInstallDirTest
+ */
+public class JPackageCreateInstallerInstallDirTest {
+    private static final String TEST_NAME = "JPackageCreateInstallerInstallDirTest";
+    private static final String EXT = "rpm";
+
+    public static void main(String[] args) throws Exception {
+        JPackageCreateInstallerInstallDirBase.run(TEST_NAME, EXT);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/linux/rpm/JPackageCreateInstallerLicenseTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerLicenseBase
+ * @requires (os.family == "linux")
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageCreateInstallerLicenseTest
+ */
+public class JPackageCreateInstallerLicenseTest {
+    private static final String TEST_NAME = "JPackageCreateInstallerLicenseTest";
+    private static final String EXT = "rpm";
+
+    public static void main(String[] args) throws Exception {
+        JPackageCreateInstallerLicenseBase.run(TEST_NAME, EXT);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/linux/rpm/JPackageCreateInstallerLicenseTypeTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerLicenseTypeBase
+ * @requires (os.family == "linux")
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageCreateInstallerLicenseTypeTest
+ */
+public class JPackageCreateInstallerLicenseTypeTest {
+    private static final String TEST_NAME = "JPackageCreateInstallerLicenseTypeTest";
+    private static final String EXT = "rpm";
+
+    public static void main(String[] args) throws Exception {
+        JPackageCreateInstallerLicenseTypeBase.run(TEST_NAME, EXT);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/linux/rpm/JPackageCreateInstallerPackageDepsTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerPackageDepsBase
+ * @requires (os.family == "linux")
+ * @modules jdk.jpackage
+ * @run main/othervm/timeout=240 -Xmx512m JPackageCreateInstallerPackageDepsTest
+ */
+public class JPackageCreateInstallerPackageDepsTest {
+    private static final String TEST_NAME = "JPackageCreateInstallerPackageDepsTest";
+    private static final String EXT = "rpm";
+
+    public static void main(String[] args) throws Exception {
+        JPackageCreateInstallerPackageDepsBase.run(TEST_NAME, EXT);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/linux/rpm/JPackageCreateInstallerTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerBase
+ * @requires (os.family == "linux")
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageCreateInstallerTest
+ */
+public class JPackageCreateInstallerTest {
+    private static final String TEST_NAME = "JPackageCreateInstallerTest";
+    private static final String EXT = "rpm";
+
+    public static void main(String[] args) throws Exception {
+        JPackageCreateInstallerBase.run(TEST_NAME, EXT);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/linux/rpm/install.sh	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,8 @@
+sudo rpm --install jpackagecreateinstallerfileassociationstest-1.0-1.x86_64.rpm
+sudo rpm --install jpackagecreateinstallerinstalldirtest-1.0-1.x86_64.rpm
+sudo rpm --install jpackagecreateinstallerlicensetest-1.0-1.x86_64.rpm
+sudo rpm --install jpackagecreateinstallerlicensetypetest-1.0-1.x86_64.rpm
+sudo rpm --install jpackagecreateinstallerpackagedepstestdep-1.0-1.x86_64.rpm
+sudo rpm --install jpackagecreateinstallerpackagedepstest-1.0-1.x86_64.rpm
+sudo rpm --install jpackagecreateinstallertest-1.0-1.x86_64.rpm
+sudo rpm --install jpackage-test-bundle-name-1.0-1.x86_64.rpm
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/linux/rpm/uninstall.sh	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,8 @@
+sudo rpm -e jpackagecreateinstallerfileassociationstest
+sudo rpm -e jpackagecreateinstallerinstalldirtest
+sudo rpm -e jpackagecreateinstallerlicensetest
+sudo rpm -e jpackagecreateinstallerlicensetypetest
+sudo rpm -e jpackagecreateinstallerpackagedepstest
+sudo rpm -e jpackagecreateinstallerpackagedepstestdep
+sudo rpm -e jpackagecreateinstallertest
+sudo rpm -e jpackage-test-bundle-name
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/macosx/base/JPackageCreateInstallerBase.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+public class JPackageCreateInstallerBase {
+
+    private static String TEST_NAME;
+    private static String EXT;
+    private static String OUTPUT;
+    private static String[] CMD;
+
+    private static void copyResults() throws Exception {
+        List<String> files = new ArrayList<>();
+        files.add(OUTPUT);
+        JPackageInstallerHelper.copyTestResults(files);
+    }
+
+    private static void testCreateInstaller() throws Exception {
+        JPackageHelper.executeCLI(true, CMD);
+        JPackageInstallerHelper.validateOutput(OUTPUT);
+        copyResults();
+    }
+
+    private static void verifyInstall() throws Exception {
+        String app = JPackagePath.getOSXInstalledApp(TEST_NAME);
+        JPackageInstallerHelper.validateApp(app);
+    }
+
+    private static void verifyUnInstall() throws Exception {
+        // Not needed on OS X, since we just deleting installed application
+        // without using generated installer. We keeping this for consistnency
+        // between platforms.
+    }
+
+    private static void init(String name, String ext) {
+        TEST_NAME = name;
+        EXT = ext;
+        OUTPUT = "output" + File.separator + TEST_NAME + "-1.0." + EXT;
+        CMD = new String[]{
+            "create-installer",
+            "--installer-type", EXT,
+            "--input", "input",
+            "--output", "output",
+            "--name", TEST_NAME,
+            "--main-jar", "hello.jar",
+            "--main-class", "Hello",
+            "--overwrite",
+            "--files", "hello.jar"};
+    }
+
+    public static void run(String name, String ext) throws Exception {
+        init(name, ext);
+
+        if (JPackageInstallerHelper.isVerifyInstall()) {
+            verifyInstall();
+        } else if (JPackageInstallerHelper.isVerifyUnInstall()) {
+            verifyUnInstall();
+        } else {
+            JPackageHelper.createHelloInstallerJar();
+            testCreateInstaller();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/macosx/base/JPackageCreateInstallerFileAssociationsBase.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.awt.Desktop;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.PrintWriter;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.List;
+
+public class JPackageCreateInstallerFileAssociationsBase {
+
+    private static String TEST_NAME;
+    private static String EXT;
+    private static String TEST_EXT;
+    private static String OUTPUT;
+    private static String[] CMD;
+
+    private static void copyResults() throws Exception {
+        List<String> files = new ArrayList<>();
+        files.add(OUTPUT);
+        JPackageInstallerHelper.copyTestResults(files);
+    }
+
+    private static void testCreateInstaller() throws Exception {
+        JPackageHelper.executeCLI(true, CMD);
+        JPackageInstallerHelper.validateOutput(OUTPUT);
+        copyResults();
+    }
+
+    private static void validateAppOutput() throws Exception {
+        File outFile = new File("appOutput.txt");
+        int count = 10;
+        boolean bOutputCreated = false;
+        while (count > 0) {
+            if (!outFile.exists()) {
+                Thread.sleep(3000);
+                count--;
+            } else {
+                bOutputCreated = true;
+                break;
+            }
+        }
+
+        if (!bOutputCreated) {
+            throw new AssertionError(outFile.getAbsolutePath() + " was not created");
+        }
+
+        String output = Files.readString(outFile.toPath());
+        String[] result = output.split("\n");
+        if (result.length != 3) {
+            System.err.println(output);
+            throw new AssertionError(
+                    "Unexpected number of lines: " + result.length);
+        }
+
+        if (!result[0].trim().equals("jpackage test application")) {
+            throw new AssertionError("Unexpected result[0]: " + result[0]);
+        }
+
+        if (!result[1].trim().equals("args.length: 1")) {
+            throw new AssertionError("Unexpected result[1]: " + result[1]);
+        }
+
+        File faFile = new File(TEST_NAME + "." + TEST_EXT);
+        if (!result[2].trim().equals(faFile.getAbsolutePath())) {
+            throw new AssertionError("Unexpected result[2]: " + result[2]);
+        }
+    }
+
+    private static void verifyInstall() throws Exception {
+        createFileAssociationsTestFile();
+        Desktop.getDesktop().open(new File(TEST_NAME + "." + TEST_EXT));
+        validateAppOutput();
+    }
+
+    private static void verifyUnInstall() throws Exception {
+        // Not needed on OS X, since we just deleting installed application
+        // without using generated installer. We keeping this for consistnency
+        // between platforms.
+    }
+
+    private static void createFileAssociationsTestFile() throws Exception {
+        try (PrintWriter out = new PrintWriter(new BufferedWriter(
+                new FileWriter(TEST_NAME + "." + TEST_EXT)))) {
+            out.println(TEST_NAME);
+        }
+    }
+
+    private static void createFileAssociationsProperties() throws Exception {
+        try (PrintWriter out = new PrintWriter(new BufferedWriter(
+                new FileWriter("fa.properties")))) {
+            out.println("extension=" + TEST_EXT);
+            out.println("mime-type=application/" + TEST_EXT);
+            out.println("description=jpackage test extention");
+        }
+    }
+
+    private static void init(String name, String ext) {
+        TEST_NAME = name;
+        EXT = ext;
+        TEST_EXT = "jptest1";
+        OUTPUT = "output" + File.separator + TEST_NAME + "-1.0." + EXT;
+        CMD = new String[]{
+            "create-installer",
+            "--installer-type", EXT,
+            "--input", "input",
+            "--output", "output",
+            "--name", TEST_NAME,
+            "--main-jar", "hello.jar",
+            "--main-class", "Hello",
+            "--overwrite",
+            "--files", "hello.jar",
+            "--file-associations", "fa.properties"};
+    }
+
+    public static void run(String name, String ext) throws Exception {
+        init(name, ext);
+
+        if (JPackageInstallerHelper.isVerifyInstall()) {
+            verifyInstall();
+        } else if (JPackageInstallerHelper.isVerifyUnInstall()) {
+            verifyUnInstall();
+        } else {
+            JPackageHelper.createHelloInstallerJar();
+            createFileAssociationsProperties();
+            testCreateInstaller();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/macosx/base/JPackageCreateInstallerInstallDirBase.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+public class JPackageCreateInstallerInstallDirBase {
+
+    private static String TEST_NAME;
+    private static String EXT;
+    private static String OUTPUT;
+    private static String[] CMD;
+
+    private static void copyResults() throws Exception {
+        List<String> files = new ArrayList<>();
+        files.add(OUTPUT);
+        JPackageInstallerHelper.copyTestResults(files);
+    }
+
+    private static void testCreateInstaller() throws Exception {
+        JPackageHelper.executeCLI(true, CMD);
+        JPackageInstallerHelper.validateOutput(OUTPUT);
+        copyResults();
+    }
+
+    private static void verifyInstall() throws Exception {
+        String app = JPackagePath.getOSXInstalledApp("jpackage", TEST_NAME);
+        JPackageInstallerHelper.validateApp(app);
+    }
+
+    private static void verifyUnInstall() throws Exception {
+        // Not needed on OS X, since we just deleting installed application
+        // without using generated installer. We keeping this for consistnency
+        // between platforms.
+    }
+
+    private static void init(String name, String ext) {
+        TEST_NAME = name;
+        EXT = ext;
+        OUTPUT = "output" + File.separator + TEST_NAME + "-1.0." + EXT;
+        CMD = new String[]{
+            "create-installer",
+            "--installer-type", EXT,
+            "--input", "input",
+            "--output", "output",
+            "--name", TEST_NAME,
+            "--main-jar", "hello.jar",
+            "--main-class", "Hello",
+            "--overwrite",
+            "--files", "hello.jar",
+            "--install-dir", "/Applications/jpackage"};
+    }
+
+    public static void run(String name, String ext) throws Exception {
+        init(name, ext);
+
+        if (JPackageInstallerHelper.isVerifyInstall()) {
+            verifyInstall();
+        } else if (JPackageInstallerHelper.isVerifyUnInstall()) {
+            verifyUnInstall();
+        } else {
+            JPackageHelper.createHelloInstallerJar();
+            testCreateInstaller();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/macosx/base/JPackageCreateInstallerLicenseBase.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+public class JPackageCreateInstallerLicenseBase {
+
+    private static String TEST_NAME;
+    private static String EXT;
+    private static String OUTPUT;
+    private static String[] CMD;
+
+    private static void copyResults() throws Exception {
+        List<String> files = new ArrayList<>();
+        files.add(OUTPUT);
+        JPackageInstallerHelper.copyTestResults(files);
+    }
+
+    private static void testCreateInstaller() throws Exception {
+        JPackageHelper.executeCLI(true, CMD);
+        JPackageInstallerHelper.validateOutput(OUTPUT);
+        copyResults();
+    }
+
+    private static void verifyInstall() throws Exception {
+        String app = JPackagePath.getOSXInstalledApp(TEST_NAME);
+        JPackageInstallerHelper.validateApp(app);
+
+    }
+
+    private static void verifyUnInstall() throws Exception {
+        // Not needed on OS X, since we just deleting installed application
+        // without using generated installer. We keeping this for consistnency
+        // between platforms.
+    }
+
+    private static void init(String name, String ext) {
+        TEST_NAME = name;
+        EXT = ext;
+        OUTPUT = "output" + File.separator + TEST_NAME + "-1.0." + EXT;
+        CMD = new String [] {
+        "create-installer",
+        "--installer-type", EXT,
+        "--input", "input",
+        "--output", "output",
+        "--name", TEST_NAME,
+        "--main-jar", "hello.jar",
+        "--main-class", "Hello",
+        "--overwrite",
+        "--files", "hello.jar",
+        "--license-file", JPackagePath.getLicenseFilePath()};
+    }
+
+    public static void run(String name, String ext) throws Exception {
+        init(name, ext);
+
+        if (JPackageInstallerHelper.isVerifyInstall()) {
+            verifyInstall();
+        } else if (JPackageInstallerHelper.isVerifyUnInstall()) {
+            verifyUnInstall();
+        } else {
+            JPackageHelper.createHelloInstallerJar();
+            testCreateInstaller();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/macosx/dmg/JPackageCreateInstallerFileAssociationsTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerFileAssociationsBase
+ * @requires (os.family == "mac")
+ * @modules jdk.jpackage
+ * @ignore
+ * @run main/othervm -Xmx512m JPackageCreateInstallerFileAssociationsTest
+ */
+public class JPackageCreateInstallerFileAssociationsTest {
+    private static final String TEST_NAME = "JPackageCreateInstallerFileAssociationsTest";
+    private static final String EXT = "dmg";
+
+    public static void main(String[] args) throws Exception {
+        JPackageCreateInstallerFileAssociationsBase.run(TEST_NAME, EXT);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/macosx/dmg/JPackageCreateInstallerInstallDirTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerInstallDirBase
+ * @requires (os.family == "mac")
+ * @modules jdk.jpackage
+ * @ignore
+ * @run main/othervm -Xmx512m JPackageCreateInstallerInstallDirTest
+ */
+public class JPackageCreateInstallerInstallDirTest {
+    private static final String TEST_NAME = "JPackageCreateInstallerInstallDirTest";
+    private static final String EXT = "dmg";
+
+    public static void main(String[] args) throws Exception {
+        JPackageCreateInstallerInstallDirBase.run(TEST_NAME, EXT);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/macosx/dmg/JPackageCreateInstallerLicenseTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerLicenseBase
+ * @requires (os.family == "mac")
+ * @modules jdk.jpackage
+ * @ignore
+ * @run main/othervm -Xmx512m JPackageCreateInstallerLicenseTest
+ */
+public class JPackageCreateInstallerLicenseTest {
+    private static final String TEST_NAME = "JPackageCreateInstallerLicenseTest";
+    private static final String EXT = "dmg";
+
+    public static void main(String[] args) throws Exception {
+        JPackageCreateInstallerLicenseBase.run(TEST_NAME, EXT);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/macosx/dmg/JPackageCreateInstallerTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerBase
+ * @requires (os.family == "mac")
+ * @modules jdk.jpackage
+ * @ignore
+ * @run main/othervm -Xmx512m JPackageCreateInstallerTest
+ */
+public class JPackageCreateInstallerTest {
+    private static final String TEST_NAME = "JPackageCreateInstallerTest";
+    private static final String EXT = "dmg";
+
+    public static void main(String[] args) throws Exception {
+        JPackageCreateInstallerBase.run(TEST_NAME, EXT);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/macosx/dmg/install.sh	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,21 @@
+echo "Note: This script will install DMG files silently. In order to verify UI, each .dmg needs to launched manually via Finder."
+
+# JPackageCreateInstallerTest
+hdiutil attach JPackageCreateInstallerTest-1.0.dmg
+sudo /usr/sbin/installer -pkg /Volumes/JPackageCreateInstallerTest/JPackageCreateInstallerTest-1.0.pkg -target /
+hdiutil detach /Volumes/JPackageCreateInstallerTest/
+
+# JPackageCreateInstallerLicenseTest
+hdiutil attach JPackageCreateInstallerLicenseTest-1.0.dmg
+sudo /usr/sbin/installer -pkg /Volumes/JPackageCreateInstallerLicenseTest/JPackageCreateInstallerLicenseTest-1.0.pkg -target /
+hdiutil detach /Volumes/JPackageCreateInstallerLicenseTest/
+
+# JPackageCreateInstallerFileAssociationsTest
+hdiutil attach JPackageCreateInstallerFileAssociationsTest-1.0.dmg
+sudo /usr/sbin/installer -pkg /Volumes/JPackageCreateInstallerFileAssociationsTest/JPackageCreateInstallerFileAssociationsTest-1.0.pkg -target /
+hdiutil detach /Volumes/JPackageCreateInstallerFileAssociationsTest/
+
+# JPackageCreateInstallerInstallDirTest
+hdiutil attach JPackageCreateInstallerInstallDirTest-1.0.dmg
+sudo /usr/sbin/installer -pkg /Volumes/JPackageCreateInstallerInstallDirTest/JPackageCreateInstallerInstallDirTest-1.0.pkg -target /
+hdiutil detach /Volumes/JPackageCreateInstallerInstallDirTest/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/macosx/dmg/uninstall.sh	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,4 @@
+sudo rm -rf /Applications/JPackageCreateInstallerTest.app
+sudo rm -rf /Applications/JPackageCreateInstallerLicenseTest.app
+sudo rm -rf /Applications/JPackageCreateInstallerFileAssociationsTest.app
+sudo rm -rf /Applications/jpackage/JPackageCreateInstallerInstallDirTest.app
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/macosx/pkg/JPackageCreateInstallerFileAssociationsTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerFileAssociationsBase
+ * @requires (os.family == "mac")
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageCreateInstallerFileAssociationsTest
+ */
+public class JPackageCreateInstallerFileAssociationsTest {
+    private static final String TEST_NAME = "JPackageCreateInstallerFileAssociationsTest";
+    private static final String EXT = "pkg";
+
+    public static void main(String[] args) throws Exception {
+        JPackageCreateInstallerFileAssociationsBase.run(TEST_NAME, EXT);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/macosx/pkg/JPackageCreateInstallerInstallDirTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerInstallDirBase
+ * @requires (os.family == "mac")
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageCreateInstallerInstallDirTest
+ */
+public class JPackageCreateInstallerInstallDirTest {
+    private static final String TEST_NAME = "JPackageCreateInstallerInstallDirTest";
+    private static final String EXT = "pkg";
+
+    public static void main(String[] args) throws Exception {
+        JPackageCreateInstallerInstallDirBase.run(TEST_NAME, EXT);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/macosx/pkg/JPackageCreateInstallerLicenseTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerLicenseBase
+ * @requires (os.family == "mac")
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageCreateInstallerLicenseTest
+ */
+public class JPackageCreateInstallerLicenseTest {
+    private static final String TEST_NAME = "JPackageCreateInstallerLicenseTest";
+    private static final String EXT = "pkg";
+
+    public static void main(String[] args) throws Exception {
+        JPackageCreateInstallerLicenseBase.run(TEST_NAME, EXT);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/macosx/pkg/JPackageCreateInstallerTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerBase
+ * @requires (os.family == "mac")
+ * @modules jdk.jpackage
+ * @run main/othervm -Xmx512m JPackageCreateInstallerTest
+ */
+public class JPackageCreateInstallerTest {
+    private static final String TEST_NAME = "JPackageCreateInstallerTest";
+    private static final String EXT = "pkg";
+
+    public static void main(String[] args) throws Exception {
+        JPackageCreateInstallerBase.run(TEST_NAME, EXT);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/macosx/pkg/install.sh	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,5 @@
+echo Note: This script will install packages silently. In order to verify UI, each .pkg needs to launched manually via Finder.
+sudo /usr/sbin/installer -pkg JPackageCreateInstallerTest-1.0.pkg -target /
+sudo /usr/sbin/installer -pkg JPackageCreateInstallerLicenseTest-1.0.pkg -target /
+sudo /usr/sbin/installer -pkg JPackageCreateInstallerFileAssociationsTest-1.0.pkg -target /
+sudo /usr/sbin/installer -pkg JPackageCreateInstallerInstallDirTest-1.0.pkg -target /
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/macosx/pkg/uninstall.sh	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,4 @@
+sudo rm -rf /Applications/JPackageCreateInstallerTest.app
+sudo rm -rf /Applications/JPackageCreateInstallerLicenseTest.app
+sudo rm -rf /Applications/JPackageCreateInstallerFileAssociationsTest.app
+sudo rm -rf /Applications/jpackage/JPackageCreateInstallerInstallDirTest.app
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/base/JPackageCreateInstallerBase.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+public class JPackageCreateInstallerBase {
+
+    private static String TEST_NAME;
+    private static String EXT;
+    private static String OUTPUT;
+    private static String[] CMD;
+
+    private static void copyResults() throws Exception {
+        List<String> files = new ArrayList<>();
+        files.add(OUTPUT);
+        JPackageInstallerHelper.copyTestResults(files);
+    }
+
+    private static void testCreateInstaller() throws Exception {
+        JPackageHelper.executeCLI(true, CMD);
+        JPackageInstallerHelper.validateOutput(OUTPUT);
+        copyResults();
+    }
+
+    private static void verifyInstall() throws Exception {
+        String app = JPackagePath.getWinInstalledApp(TEST_NAME);
+        JPackageInstallerHelper.validateApp(app);
+
+        // Validate start menu
+        JPackageInstallerHelper.validateStartMenu("Unknown", TEST_NAME, true);
+    }
+
+    private static void verifyUnInstall() throws Exception {
+        String folderPath = JPackagePath.getWinInstallFolder(TEST_NAME);
+        File folder = new File(folderPath);
+        if (folder.exists()) {
+            throw new AssertionError("Error: " + folder.getAbsolutePath() + " exist");
+        }
+
+        // Validate start menu
+        JPackageInstallerHelper.validateStartMenu("Unknown", TEST_NAME, false);
+    }
+
+    private static void init(String name, String ext) {
+        TEST_NAME = name;
+        EXT = ext;
+        OUTPUT = "output" + File.separator + TEST_NAME + "-1.0." + EXT;
+        CMD = new String[]{
+            "create-installer",
+            "--installer-type", EXT,
+            "--input", "input",
+            "--output", "output",
+            "--name", TEST_NAME,
+            "--main-jar", "hello.jar",
+            "--main-class", "Hello",
+            "--overwrite",
+            "--files", "hello.jar"};
+    }
+
+    public static void run(String name, String ext) throws Exception {
+        init(name, ext);
+
+        if (JPackageInstallerHelper.isVerifyInstall()) {
+            verifyInstall();
+        } else if (JPackageInstallerHelper.isVerifyUnInstall()) {
+            verifyUnInstall();
+        } else {
+            JPackageHelper.createHelloInstallerJar();
+            testCreateInstaller();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/base/JPackageCreateInstallerFileAssociationsBase.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,170 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.awt.Desktop;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.PrintWriter;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.List;
+
+public class JPackageCreateInstallerFileAssociationsBase {
+
+    private static String TEST_NAME;
+    private static String EXT;
+    private static String TEST_EXT;
+    private static String OUTPUT;
+    private static String[] CMD;
+
+    private static void copyResults() throws Exception {
+        List<String> files = new ArrayList<>();
+        files.add(OUTPUT);
+        JPackageInstallerHelper.copyTestResults(files);
+    }
+
+    private static void testCreateInstaller() throws Exception {
+        JPackageHelper.executeCLI(true, CMD);
+        JPackageInstallerHelper.validateOutput(OUTPUT);
+        copyResults();
+    }
+
+    private static void validateAppOutput() throws Exception {
+        File outFile = new File("appOutput.txt");
+        int count = 10;
+        boolean bOutputCreated = false;
+        while (count > 0) {
+            if (!outFile.exists()) {
+                Thread.sleep(3000);
+                count--;
+            } else {
+                bOutputCreated = true;
+                break;
+            }
+        }
+
+        if (!bOutputCreated) {
+            throw new AssertionError(outFile.getAbsolutePath() + " was not created");
+        }
+
+        String output = Files.readString(outFile.toPath());
+        String[] result = output.split("\n");
+        if (result.length != 3) {
+            System.err.println(output);
+            throw new AssertionError(
+                    "Unexpected number of lines: " + result.length);
+        }
+
+        if (!result[0].trim().equals("jpackage test application")) {
+            throw new AssertionError("Unexpected result[0]: " + result[0]);
+        }
+
+        if (!result[1].trim().equals("args.length: 1")) {
+            throw new AssertionError("Unexpected result[1]: " + result[1]);
+        }
+
+        File faFile = new File(TEST_NAME + "." + TEST_EXT);
+        if (!result[2].trim().equals(faFile.getAbsolutePath())) {
+            throw new AssertionError("Unexpected result[2]: " + result[2]);
+        }
+    }
+
+    private static void verifyInstall() throws Exception {
+        createFileAssociationsTestFile();
+        Desktop.getDesktop().open(new File(TEST_NAME + "." + TEST_EXT));
+        validateAppOutput();
+
+        // Validate start menu
+        JPackageInstallerHelper.validateStartMenu("Unknown", TEST_NAME, true);
+
+        // Validate registry
+        String[] values1 = {TEST_NAME};
+        JPackageInstallerHelper.validateWinRegistry("HKLM\\Software\\Classes\\." + TEST_EXT, values1, true);
+        String[] values2 = {TEST_EXT};
+        JPackageInstallerHelper.validateWinRegistry("HKLM\\Software\\Classes\\MIME\\Database\\Content Type\\application/" + TEST_EXT, values2, true);
+    }
+
+    private static void verifyUnInstall() throws Exception {
+        String folderPath = JPackagePath.getWinInstallFolder(TEST_NAME);
+        File folder = new File(folderPath);
+        if (folder.exists()) {
+            throw new AssertionError("Error: " + folder.getAbsolutePath() + " exist");
+        }
+
+        // Validate start menu
+        JPackageInstallerHelper.validateStartMenu("Unknown", TEST_NAME, false);
+
+        // Validate registry
+        JPackageInstallerHelper.validateWinRegistry("HKLM\\Software\\Classes\\." + TEST_EXT, null, false);
+        JPackageInstallerHelper.validateWinRegistry("HKLM\\Software\\Classes\\MIME\\Database\\Content Type\\application/" + TEST_EXT, null, false);
+    }
+
+    private static void createFileAssociationsTestFile() throws Exception {
+        try (PrintWriter out = new PrintWriter(new BufferedWriter(
+                new FileWriter(TEST_NAME + "." + TEST_EXT)))) {
+            out.println(TEST_NAME);
+        }
+    }
+
+    private static void createFileAssociationsProperties() throws Exception {
+        try (PrintWriter out = new PrintWriter(new BufferedWriter(
+                new FileWriter("fa.properties")))) {
+            out.println("extension=" + TEST_EXT);
+            out.println("mime-type=application/" + TEST_EXT);
+            out.println("description=jpackage test extention");
+        }
+    }
+
+    private static void init(String name, String ext) {
+        TEST_NAME = name;
+        EXT = ext;
+        TEST_EXT = "jptest1";
+        OUTPUT = "output" + File.separator + TEST_NAME + "-1.0." + EXT;
+        CMD = new String[]{
+            "create-installer",
+            "--installer-type", EXT,
+            "--input", "input",
+            "--output", "output",
+            "--name", TEST_NAME,
+            "--main-jar", "hello.jar",
+            "--main-class", "Hello",
+            "--overwrite",
+            "--files", "hello.jar",
+            "--file-associations", "fa.properties"};
+    }
+
+    public static void run(String name, String ext) throws Exception {
+        init(name, ext);
+
+        if (JPackageInstallerHelper.isVerifyInstall()) {
+            verifyInstall();
+        } else if (JPackageInstallerHelper.isVerifyUnInstall()) {
+            verifyUnInstall();
+        } else {
+            JPackageHelper.createHelloInstallerJar();
+            createFileAssociationsProperties();
+            testCreateInstaller();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/base/JPackageCreateInstallerLicenseBase.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+public class JPackageCreateInstallerLicenseBase {
+
+    private static String TEST_NAME;
+    private static String EXT;
+    private static String OUTPUT;
+    private static String[] CMD;
+
+    private static void copyResults() throws Exception {
+        List<String> files = new ArrayList<>();
+        files.add(OUTPUT);
+        JPackageInstallerHelper.copyTestResults(files);
+    }
+
+    private static void testCreateInstaller() throws Exception {
+        JPackageHelper.executeCLI(true, CMD);
+        JPackageInstallerHelper.validateOutput(OUTPUT);
+        copyResults();
+    }
+
+    private static void verifyInstall() throws Exception {
+        String app = JPackagePath.getWinInstalledApp(TEST_NAME);
+        JPackageInstallerHelper.validateApp(app);
+
+        // Validate start menu
+        JPackageInstallerHelper.validateStartMenu("Unknown", TEST_NAME, true);
+    }
+
+    private static void verifyUnInstall() throws Exception {
+        String folderPath = JPackagePath.getWinInstallFolder(TEST_NAME);
+        File folder = new File(folderPath);
+        if (folder.exists()) {
+            throw new AssertionError("Error: " + folder.getAbsolutePath() + " exist");
+        }
+
+        // Validate start menu
+        JPackageInstallerHelper.validateStartMenu("Unknown", TEST_NAME, false);
+    }
+
+    private static void init(String name, String ext) {
+        TEST_NAME = name;
+        EXT = ext;
+        OUTPUT = "output" + File.separator + TEST_NAME + "-1.0." + EXT;
+        CMD = new String [] {
+        "create-installer",
+        "--installer-type", EXT,
+        "--input", "input",
+        "--output", "output",
+        "--name", TEST_NAME,
+        "--main-jar", "hello.jar",
+        "--main-class", "Hello",
+        "--overwrite",
+        "--files", "hello.jar",
+        "--license-file", JPackagePath.getLicenseFilePath()};
+    }
+
+    public static void run(String name, String ext) throws Exception {
+        init(name, ext);
+
+        if (JPackageInstallerHelper.isVerifyInstall()) {
+            verifyInstall();
+        } else if (JPackageInstallerHelper.isVerifyUnInstall()) {
+            verifyUnInstall();
+        } else {
+            JPackageHelper.createHelloInstallerJar();
+            testCreateInstaller();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/base/JPackageCreateInstallerWinDirChooserBase.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+public class JPackageCreateInstallerWinDirChooserBase {
+
+    private static String TEST_NAME;
+    private static String EXT;
+    private static String OUTPUT;
+    private static String[] CMD;
+
+    private static void copyResults() throws Exception {
+        List<String> files = new ArrayList<>();
+        files.add(OUTPUT);
+        JPackageInstallerHelper.copyTestResults(files);
+    }
+
+    private static void testCreateInstaller() throws Exception {
+        JPackageHelper.executeCLI(true, CMD);
+        JPackageInstallerHelper.validateOutput(OUTPUT);
+        copyResults();
+    }
+
+    private static void verifyInstall() throws Exception {
+        String app = JPackagePath.getWinInstalledApp(TEST_NAME);
+        JPackageInstallerHelper.validateApp(app);
+
+        // Validate start menu
+        JPackageInstallerHelper.validateStartMenu("Unknown", TEST_NAME, true);
+    }
+
+    private static void verifyUnInstall() throws Exception {
+        String folderPath = JPackagePath.getWinInstallFolder(TEST_NAME);
+        File folder = new File(folderPath);
+        if (folder.exists()) {
+            throw new AssertionError("Error: " + folder.getAbsolutePath() + " exist");
+        }
+
+        // Validate start menu
+        JPackageInstallerHelper.validateStartMenu("Unknown", TEST_NAME, false);
+    }
+
+    private static void init(String name, String ext) {
+        TEST_NAME = name;
+        EXT = ext;
+        OUTPUT = "output" + File.separator + TEST_NAME + "-1.0." + EXT;
+        CMD = new String[]{
+            "create-installer",
+            "--installer-type", EXT,
+            "--input", "input",
+            "--output", "output",
+            "--name", TEST_NAME,
+            "--main-jar", "hello.jar",
+            "--main-class", "Hello",
+            "--overwrite",
+            "--files", "hello.jar",
+            "--win-dir-chooser"};
+    }
+
+    public static void run(String name, String ext) throws Exception {
+        init(name, ext);
+
+        if (JPackageInstallerHelper.isVerifyInstall()) {
+            verifyInstall();
+        } else if (JPackageInstallerHelper.isVerifyUnInstall()) {
+            verifyUnInstall();
+        } else {
+            JPackageHelper.createHelloInstallerJar();
+            testCreateInstaller();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/base/JPackageCreateInstallerWinMenuBase.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+public class JPackageCreateInstallerWinMenuBase {
+
+    private static String TEST_NAME;
+    private static String EXT;
+    private static String OUTPUT;
+    private static String[] CMD;
+
+    private static void copyResults() throws Exception {
+        List<String> files = new ArrayList<>();
+        files.add(OUTPUT);
+        JPackageInstallerHelper.copyTestResults(files);
+    }
+
+    private static void testCreateInstaller() throws Exception {
+        JPackageHelper.executeCLI(true, CMD);
+        JPackageInstallerHelper.validateOutput(OUTPUT);
+        copyResults();
+    }
+
+    private static void verifyInstall() throws Exception {
+        String app = JPackagePath.getWinInstalledApp(TEST_NAME);
+        JPackageInstallerHelper.validateApp(app);
+
+        // Validate start menu
+        JPackageInstallerHelper.validateStartMenu("Unknown", TEST_NAME, true);
+    }
+
+    private static void verifyUnInstall() throws Exception {
+        String folderPath = JPackagePath.getWinInstallFolder(TEST_NAME);
+        File folder = new File(folderPath);
+        if (folder.exists()) {
+            throw new AssertionError("Error: " + folder.getAbsolutePath() + " exist");
+        }
+
+        // Validate start menu
+        JPackageInstallerHelper.validateStartMenu("Unknown", TEST_NAME, false);
+    }
+
+    private static void init(String name, String ext) {
+        TEST_NAME = name;
+        EXT = ext;
+        OUTPUT = "output" + File.separator + TEST_NAME + "-1.0." + EXT;
+        CMD = new String[]{
+            "create-installer",
+            "--installer-type", EXT,
+            "--input", "input",
+            "--output", "output",
+            "--name", TEST_NAME,
+            "--main-jar", "hello.jar",
+            "--main-class", "Hello",
+            "--overwrite",
+            "--files", "hello.jar",
+            "--win-menu"};
+    }
+
+    public static void run(String name, String ext) throws Exception {
+        init(name, ext);
+
+        if (JPackageInstallerHelper.isVerifyInstall()) {
+            verifyInstall();
+        } else if (JPackageInstallerHelper.isVerifyUnInstall()) {
+            verifyUnInstall();
+        } else {
+            JPackageHelper.createHelloInstallerJar();
+            testCreateInstaller();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/base/JPackageCreateInstallerWinMenuGroupBase.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+public class JPackageCreateInstallerWinMenuGroupBase {
+
+    private static String TEST_NAME;
+    private static String EXT;
+    private static String OUTPUT;
+    private static String[] CMD;
+
+    private static void copyResults() throws Exception {
+        List<String> files = new ArrayList<>();
+        files.add(OUTPUT);
+        JPackageInstallerHelper.copyTestResults(files);
+    }
+
+    private static void testCreateInstaller() throws Exception {
+        JPackageHelper.executeCLI(true, CMD);
+        JPackageInstallerHelper.validateOutput(OUTPUT);
+        copyResults();
+    }
+
+    private static void verifyInstall() throws Exception {
+        String app = JPackagePath.getWinInstalledApp(TEST_NAME);
+        JPackageInstallerHelper.validateApp(app);
+
+        // Validate start menu
+        JPackageInstallerHelper.validateStartMenu(TEST_NAME, TEST_NAME, true);
+    }
+
+    private static void verifyUnInstall() throws Exception {
+        String folderPath = JPackagePath.getWinInstallFolder(TEST_NAME);
+        File folder = new File(folderPath);
+        if (folder.exists()) {
+            throw new AssertionError("Error: " + folder.getAbsolutePath() + " exist");
+        }
+
+        // Validate start menu
+        JPackageInstallerHelper.validateStartMenu(TEST_NAME, TEST_NAME, false);
+    }
+
+    private static void init(String name, String ext) {
+        TEST_NAME = name;
+        EXT = ext;
+        OUTPUT = "output" + File.separator + TEST_NAME + "-1.0." + EXT;
+        CMD = new String[]{
+            "create-installer",
+            "--installer-type", EXT,
+            "--input", "input",
+            "--output", "output",
+            "--name", TEST_NAME,
+            "--main-jar", "hello.jar",
+            "--main-class", "Hello",
+            "--overwrite",
+            "--files", "hello.jar",
+            "--win-menu",
+            "--win-menu-group", TEST_NAME};
+    }
+
+    public static void run(String name, String ext) throws Exception {
+        init(name, ext);
+
+        if (JPackageInstallerHelper.isVerifyInstall()) {
+            verifyInstall();
+        } else if (JPackageInstallerHelper.isVerifyUnInstall()) {
+            verifyUnInstall();
+        } else {
+            JPackageHelper.createHelloInstallerJar();
+            testCreateInstaller();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/base/JPackageCreateInstallerWinPerUserInstallBase.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+public class JPackageCreateInstallerWinPerUserInstallBase {
+
+    private static String TEST_NAME;
+    private static String EXT;
+    private static String OUTPUT;
+    private static String[] CMD;
+
+    private static void copyResults() throws Exception {
+        List<String> files = new ArrayList<>();
+        files.add(OUTPUT);
+        JPackageInstallerHelper.copyTestResults(files);
+    }
+
+    private static void testCreateInstaller() throws Exception {
+        JPackageHelper.executeCLI(true, CMD);
+        JPackageInstallerHelper.validateOutput(OUTPUT);
+        copyResults();
+    }
+
+    private static void verifyInstall() throws Exception {
+        String app = JPackagePath.getWinUserLocalInstalledApp(TEST_NAME);
+        JPackageInstallerHelper.validateApp(app);
+
+        // Validate start menu
+        JPackageInstallerHelper.validateUserLocalStartMenu(TEST_NAME, TEST_NAME, true);
+    }
+
+    private static void verifyUnInstall() throws Exception {
+        String folderPath = JPackagePath.getWinUserLocalInstallFolder(TEST_NAME);
+        File folder = new File(folderPath);
+        if (folder.exists()) {
+            throw new AssertionError("Error: " + folder.getAbsolutePath() + " exist");
+        }
+
+        // Validate start menu
+        JPackageInstallerHelper.validateUserLocalStartMenu(TEST_NAME, TEST_NAME, false);
+    }
+
+    private static void init(String name, String ext) {
+        TEST_NAME = name;
+        EXT = ext;
+        OUTPUT = "output" + File.separator + TEST_NAME + "-1.0." + EXT;
+        CMD = new String[]{
+            "create-installer",
+            "--installer-type", EXT,
+            "--input", "input",
+            "--output", "output",
+            "--name", TEST_NAME,
+            "--main-jar", "hello.jar",
+            "--main-class", "Hello",
+            "--overwrite",
+            "--files", "hello.jar",
+            "--win-per-user-install",
+            "--win-menu",
+            "--win-menu-group", TEST_NAME};
+    }
+
+    public static void run(String name, String ext) throws Exception {
+        init(name, ext);
+
+        if (JPackageInstallerHelper.isVerifyInstall()) {
+            verifyInstall();
+        } else if (JPackageInstallerHelper.isVerifyUnInstall()) {
+            verifyUnInstall();
+        } else {
+            JPackageHelper.createHelloInstallerJar();
+            testCreateInstaller();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/base/JPackageCreateInstallerWinRegistryNameBase.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.awt.Desktop;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.PrintWriter;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.List;
+
+public class JPackageCreateInstallerWinRegistryNameBase {
+
+    private static String TEST_NAME;
+    private static String EXT;
+    private static String TEST_EXT;
+    private static String WIN_REGISTRY_NAME;
+    private static String OUTPUT;
+    private static String[] CMD;
+
+    private static void copyResults() throws Exception {
+        List<String> files = new ArrayList<>();
+        files.add(OUTPUT);
+        JPackageInstallerHelper.copyTestResults(files);
+    }
+
+    private static void testCreateInstaller() throws Exception {
+        JPackageHelper.executeCLI(true, CMD);
+        JPackageInstallerHelper.validateOutput(OUTPUT);
+        copyResults();
+    }
+
+    private static void validateAppOutput() throws Exception {
+        File outFile = new File("appOutput.txt");
+        int count = 10;
+        boolean bOutputCreated = false;
+        while (count > 0) {
+            if (!outFile.exists()) {
+                Thread.sleep(3000);
+                count--;
+            } else {
+                bOutputCreated = true;
+                break;
+            }
+        }
+
+        if (!bOutputCreated) {
+            throw new AssertionError(outFile.getAbsolutePath() + " was not created");
+        }
+
+        String output = Files.readString(outFile.toPath());
+        String[] result = output.split("\n");
+        if (result.length != 3) {
+            System.err.println(output);
+            throw new AssertionError(
+                    "Unexpected number of lines: " + result.length);
+        }
+
+        if (!result[0].trim().equals("jpackage test application")) {
+            throw new AssertionError("Unexpected result[0]: " + result[0]);
+        }
+
+        if (!result[1].trim().equals("args.length: 1")) {
+            throw new AssertionError("Unexpected result[1]: " + result[1]);
+        }
+
+        File faFile = new File(TEST_NAME + "." + TEST_EXT);
+        if (!result[2].trim().equals(faFile.getAbsolutePath())) {
+            throw new AssertionError("Unexpected result[2]: " + result[2]);
+        }
+    }
+
+    private static void verifyInstall() throws Exception {
+        createFileAssociationsTestFile();
+        Desktop.getDesktop().open(new File(TEST_NAME + "." + TEST_EXT));
+        validateAppOutput();
+
+        // Validate start menu
+        JPackageInstallerHelper.validateStartMenu("Unknown", TEST_NAME, true);
+
+        // Validate registry
+        String[] values1 = {WIN_REGISTRY_NAME};
+        JPackageInstallerHelper.validateWinRegistry("HKLM\\Software\\Classes\\." + TEST_EXT, values1, true);
+        String[] values2 = {TEST_EXT};
+        JPackageInstallerHelper.validateWinRegistry("HKLM\\Software\\Classes\\MIME\\Database\\Content Type\\application/" + TEST_EXT, values2, true);
+    }
+
+    private static void verifyUnInstall() throws Exception {
+        String folderPath = JPackagePath.getWinInstallFolder(TEST_NAME);
+        File folder = new File(folderPath);
+        if (folder.exists()) {
+            throw new AssertionError("Error: " + folder.getAbsolutePath() + " exist");
+        }
+
+        // Validate start menu
+        JPackageInstallerHelper.validateStartMenu("Unknown", TEST_NAME, false);
+
+        // Validate registry
+        JPackageInstallerHelper.validateWinRegistry("HKLM\\Software\\Classes\\." + TEST_EXT, null, false);
+        JPackageInstallerHelper.validateWinRegistry("HKLM\\Software\\Classes\\MIME\\Database\\Content Type\\application/" + TEST_EXT, null, false);
+    }
+
+    private static void createFileAssociationsTestFile() throws Exception {
+        try (PrintWriter out = new PrintWriter(new BufferedWriter(
+                new FileWriter(TEST_NAME + "." + TEST_EXT)))) {
+            out.println(TEST_NAME);
+        }
+    }
+
+    private static void createFileAssociationsProperties() throws Exception {
+        try (PrintWriter out = new PrintWriter(new BufferedWriter(
+                new FileWriter("fa.properties")))) {
+            out.println("extension=" + TEST_EXT);
+            out.println("mime-type=application/" + TEST_EXT);
+            out.println("description=jpackage test extention");
+        }
+    }
+
+    private static void init(String name, String ext) {
+        TEST_NAME = name;
+        EXT = ext;
+        TEST_EXT = "jptest2";
+        WIN_REGISTRY_NAME = "JPWINTESTREGISTRYNAME";
+        OUTPUT = "output" + File.separator + TEST_NAME + "-1.0." + EXT;
+        CMD = new String[]{
+            "create-installer",
+            "--installer-type", EXT,
+            "--input", "input",
+            "--output", "output",
+            "--name", TEST_NAME,
+            "--main-jar", "hello.jar",
+            "--main-class", "Hello",
+            "--overwrite",
+            "--files", "hello.jar",
+            "--file-associations", "fa.properties",
+            "--win-registry-name", WIN_REGISTRY_NAME};
+    }
+
+    public static void run(String name, String ext) throws Exception {
+        init(name, ext);
+
+        if (JPackageInstallerHelper.isVerifyInstall()) {
+            verifyInstall();
+        } else if (JPackageInstallerHelper.isVerifyUnInstall()) {
+            verifyUnInstall();
+        } else {
+            JPackageHelper.createHelloInstallerJar();
+            createFileAssociationsProperties();
+            testCreateInstaller();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/base/JPackageCreateInstallerWinShortcutBase.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+public class JPackageCreateInstallerWinShortcutBase {
+
+    private static String TEST_NAME;
+    private static String EXT;
+    private static String OUTPUT;
+    private static String[] CMD;
+
+    private static void copyResults() throws Exception {
+        List<String> files = new ArrayList<>();
+        files.add(OUTPUT);
+        JPackageInstallerHelper.copyTestResults(files);
+    }
+
+    private static void testCreateInstaller() throws Exception {
+        JPackageHelper.executeCLI(true, CMD);
+        JPackageInstallerHelper.validateOutput(OUTPUT);
+        copyResults();
+    }
+
+    private static void verifyInstall() throws Exception {
+        String app = JPackagePath.getWinInstalledApp(TEST_NAME);
+        JPackageInstallerHelper.validateApp(app);
+
+        // Validate start menu
+        JPackageInstallerHelper.validateStartMenu("Unknown", TEST_NAME, false);
+
+        // Validate desktop shortcut
+        JPackageInstallerHelper.validateDesktopShortcut(TEST_NAME, true);
+    }
+
+    private static void verifyUnInstall() throws Exception {
+        String folderPath = JPackagePath.getWinInstallFolder(TEST_NAME);
+        File folder = new File(folderPath);
+        if (folder.exists()) {
+            throw new AssertionError("Error: " + folder.getAbsolutePath() + " exist");
+        }
+
+        // Validate start menu
+        JPackageInstallerHelper.validateStartMenu("Unknown", TEST_NAME, false);
+
+        // Validate desktop shortcut
+        JPackageInstallerHelper.validateDesktopShortcut(TEST_NAME, false);
+    }
+
+    private static void init(String name, String ext) {
+        TEST_NAME = name;
+        EXT = ext;
+        OUTPUT = "output" + File.separator + TEST_NAME + "-1.0." + EXT;
+        CMD = new String[]{
+            "create-installer",
+            "--installer-type", EXT,
+            "--input", "input",
+            "--output", "output",
+            "--name", TEST_NAME,
+            "--main-jar", "hello.jar",
+            "--main-class", "Hello",
+            "--overwrite",
+            "--files", "hello.jar",
+            "--win-shortcut"};
+    }
+
+    public static void run(String name, String ext) throws Exception {
+        init(name, ext);
+
+        if (JPackageInstallerHelper.isVerifyInstall()) {
+            verifyInstall();
+        } else if (JPackageInstallerHelper.isVerifyUnInstall()) {
+            verifyUnInstall();
+        } else {
+            JPackageHelper.createHelloInstallerJar();
+            testCreateInstaller();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/base/JPackageCreateInstallerWinUpgradeUUIDBase.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+public class JPackageCreateInstallerWinUpgradeUUIDBase {
+
+    private static String TEST_NAME;
+    private static String EXT;
+    private static String OUTPUT_1;
+    private static String[] CMD_1;
+    private static String OUTPUT_2;
+    private static String[] CMD_2;
+    private static final String FILE_1 = "file1.txt";
+    private static final String FILE_2 = "file2.txt";
+
+    private static void copyResults() throws Exception {
+        List<String> files = new ArrayList<>();
+        files.add(OUTPUT_1);
+        files.add(OUTPUT_2);
+        JPackageInstallerHelper.copyTestResults(files);
+    }
+
+    private static void testCreateInstaller() throws Exception {
+        JPackageHelper.executeCLI(true, CMD_1);
+        JPackageInstallerHelper.validateOutput(OUTPUT_1);
+        JPackageHelper.executeCLI(true, CMD_2);
+        JPackageInstallerHelper.validateOutput(OUTPUT_2);
+        copyResults();
+    }
+
+    private static void verifyInstall() throws Exception {
+        String app = JPackagePath.getWinInstalledApp(TEST_NAME);
+        JPackageInstallerHelper.validateApp(app);
+
+        String file1Path = JPackagePath.getWinInstalledAppFolder(TEST_NAME) + File.separator + FILE_1;
+        File file1 = new File(file1Path);
+        if (EXT.equals("msi")) {
+            if (file1.exists()) {
+                throw new AssertionError("Unexpected file does exist: "
+                        + file1.getAbsolutePath());
+            }
+        } else if (EXT.equals("exe")) {
+            if (!file1.exists()) {
+                throw new AssertionError("Unexpected file does not exist: "
+                    + file1.getAbsolutePath());
+            }
+        } else {
+            throw new AssertionError("Unknown installer type: " + EXT);
+        }
+
+        String file2Path = JPackagePath.getWinInstalledAppFolder(TEST_NAME) + File.separator + FILE_2;
+        File file2 = new File(file2Path);
+        if (!file2.exists()) {
+            throw new AssertionError("Unexpected file does not exist: "
+                    + file2.getAbsolutePath());
+        }
+    }
+
+    private static void verifyUnInstall() throws Exception {
+        String folderPath = JPackagePath.getWinInstallFolder(TEST_NAME);
+        File folder = new File(folderPath);
+        if (folder.exists()) {
+            throw new AssertionError("Error: " + folder.getAbsolutePath() + " exist");
+        }
+    }
+
+    private static void init(String name, String ext) {
+        TEST_NAME = name;
+        EXT = ext;
+        OUTPUT_1 = "output" + File.separator + TEST_NAME + "-1.0." + EXT;
+        CMD_1 = new String[]{
+            "create-installer",
+            "--installer-type", EXT,
+            "--input", "input",
+            "--output", "output",
+            "--name", TEST_NAME,
+            "--main-jar", "hello.jar",
+            "--main-class", "Hello",
+            "--overwrite",
+            "--files", "hello.jar" + File.pathSeparator + FILE_1,
+            "--app-version", "1.0",
+            "--win-upgrade-uuid", "F0B18E75-52AD-41A2-BC86-6BE4FCD50BEB"};
+        OUTPUT_2 = "output" + File.separator + TEST_NAME + "-2.0." + EXT;
+        CMD_2 = new String[]{
+            "create-installer",
+            "--installer-type", EXT,
+            "--input", "input",
+            "--output", "output",
+            "--name", TEST_NAME,
+            "--main-jar", "hello.jar",
+            "--main-class", "Hello",
+            "--overwrite",
+            "--files", "hello.jar" + File.pathSeparator + FILE_2,
+            "--app-version", "2.0",
+            "--win-upgrade-uuid", "F0B18E75-52AD-41A2-BC86-6BE4FCD50BEB"};
+    }
+
+    private static void createInputFile(String name, String context) throws Exception {
+        try (PrintWriter out = new PrintWriter(
+                new BufferedWriter(new FileWriter("input" + File.separator + name)))) {
+            out.println(context);
+        }
+    }
+
+    public static void run(String name, String ext) throws Exception {
+        init(name, ext);
+
+        if (JPackageInstallerHelper.isVerifyInstall()) {
+            verifyInstall();
+        } else if (JPackageInstallerHelper.isVerifyUnInstall()) {
+            verifyUnInstall();
+        } else {
+            JPackageHelper.createHelloInstallerJar();
+            createInputFile(FILE_1, FILE_1);
+            createInputFile(FILE_2, FILE_2);
+            testCreateInstaller();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/exe/JPackageCreateInstallerFileAssociationsTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerFileAssociationsBase
+ * @requires (os.family == "windows")
+ * @modules jdk.jpackage
+ * @ignore
+ * @run main/othervm -Xmx512m JPackageCreateInstallerFileAssociationsTest
+ */
+public class JPackageCreateInstallerFileAssociationsTest {
+    private static final String TEST_NAME = "JPackageCreateInstallerFileAssociationsTest";
+    private static final String EXT = "exe";
+
+    public static void main(String[] args) throws Exception {
+        JPackageCreateInstallerFileAssociationsBase.run(TEST_NAME, EXT);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/exe/JPackageCreateInstallerLicenseTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerLicenseBase
+ * @requires (os.family == "windows")
+ * @modules jdk.jpackage
+ * @ignore
+ * @run main/othervm -Xmx512m JPackageCreateInstallerLicenseTest
+ */
+public class JPackageCreateInstallerLicenseTest {
+    private static final String TEST_NAME = "JPackageCreateInstallerLicenseTest";
+    private static final String EXT = "exe";
+
+    public static void main(String[] args) throws Exception {
+        JPackageCreateInstallerLicenseBase.run(TEST_NAME, EXT);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/exe/JPackageCreateInstallerTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerBase
+ * @requires (os.family == "windows")
+ * @modules jdk.jpackage
+ * @ignore
+ * @run main/othervm -Xmx512m JPackageCreateInstallerTest
+ */
+public class JPackageCreateInstallerTest {
+    private static final String TEST_NAME = "JPackageCreateInstallerTest";
+    private static final String EXT = "exe";
+
+    public static void main(String[] args) throws Exception {
+        JPackageCreateInstallerBase.run(TEST_NAME, EXT);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/exe/JPackageCreateInstallerWinDirChooserTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerWinDirChooserBase
+ * @requires (os.family == "windows")
+ * @modules jdk.jpackage
+ * @ignore
+ * @run main/othervm -Xmx512m JPackageCreateInstallerWinDirChooserTest
+ */
+public class JPackageCreateInstallerWinDirChooserTest {
+    private static final String TEST_NAME = "JPackageCreateInstallerWinDirChooserTest";
+    private static final String EXT = "exe";
+
+    public static void main(String[] args) throws Exception {
+        JPackageCreateInstallerWinDirChooserBase.run(TEST_NAME, EXT);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/exe/JPackageCreateInstallerWinMenuGroupTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerWinMenuGroupBase
+ * @requires (os.family == "windows")
+ * @modules jdk.jpackage
+ * @ignore
+ * @run main/othervm -Xmx512m JPackageCreateInstallerWinMenuGroupTest
+ */
+public class JPackageCreateInstallerWinMenuGroupTest {
+    private static final String TEST_NAME = "JPackageCreateInstallerWinMenuGroupTest";
+    private static final String EXT = "exe";
+
+    public static void main(String[] args) throws Exception {
+        JPackageCreateInstallerWinMenuGroupBase.run(TEST_NAME, EXT);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/exe/JPackageCreateInstallerWinMenuTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerWinMenuBase
+ * @requires (os.family == "windows")
+ * @modules jdk.jpackage
+ * @ignore
+ * @run main/othervm -Xmx512m JPackageCreateInstallerWinMenuTest
+ */
+public class JPackageCreateInstallerWinMenuTest {
+    private static final String TEST_NAME = "JPackageCreateInstallerWinMenuTest";
+    private static final String EXT = "exe";
+
+    public static void main(String[] args) throws Exception {
+        JPackageCreateInstallerWinMenuBase.run(TEST_NAME, EXT);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/exe/JPackageCreateInstallerWinPerUserInstallTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerWinPerUserInstallBase
+ * @requires (os.family == "windows")
+ * @modules jdk.jpackage
+ * @ignore
+ * @run main/othervm -Xmx512m JPackageCreateInstallerWinPerUserInstallTest
+ */
+public class JPackageCreateInstallerWinPerUserInstallTest {
+    private static final String TEST_NAME = "JPackageCreateInstallerWinPerUserInstallTest";
+    private static final String EXT = "exe";
+
+    public static void main(String[] args) throws Exception {
+        JPackageCreateInstallerWinPerUserInstallBase.run(TEST_NAME, EXT);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/exe/JPackageCreateInstallerWinRegistryNameTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerWinRegistryNameBase
+ * @requires (os.family == "windows")
+ * @modules jdk.jpackage
+ * @ignore
+ * @run main/othervm -Xmx512m JPackageCreateInstallerWinRegistryNameTest
+ */
+public class JPackageCreateInstallerWinRegistryNameTest {
+    private static final String TEST_NAME = "JPackageCreateInstallerWinRegistryNameTest";
+    private static final String EXT = "exe";
+
+    public static void main(String[] args) throws Exception {
+        JPackageCreateInstallerWinRegistryNameBase.run(TEST_NAME, EXT);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/exe/JPackageCreateInstallerWinShortcutTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerWinShortcutBase
+ * @requires (os.family == "windows")
+ * @modules jdk.jpackage
+ * @ignore
+ * @run main/othervm -Xmx512m JPackageCreateInstallerWinShortcutTest
+ */
+public class JPackageCreateInstallerWinShortcutTest {
+    private static final String TEST_NAME = "JPackageCreateInstallerWinShortcutTest";
+    private static final String EXT = "exe";
+
+    public static void main(String[] args) throws Exception {
+        JPackageCreateInstallerWinShortcutBase.run(TEST_NAME, EXT);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/exe/JPackageCreateInstallerWinUpgradeUUIDTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerWinUpgradeUUIDBase
+ * @requires (os.family == "windows")
+ * @modules jdk.jpackage
+ * @ignore
+ * @run main/othervm -Xmx512m JPackageCreateInstallerWinUpgradeUUIDTest
+ */
+public class JPackageCreateInstallerWinUpgradeUUIDTest {
+    private static final String TEST_NAME = "JPackageCreateInstallerWinUpgradeUUIDTest";
+    private static final String EXT = "exe";
+
+    public static void main(String[] args) throws Exception {
+        JPackageCreateInstallerWinUpgradeUUIDBase.run(TEST_NAME, EXT);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/exe/install.bat	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,14 @@
+JPackageCreateInstallerTest-1.0.exe
+JPackageCreateInstallerWinMenuTest-1.0.exe
+JPackageCreateInstallerWinMenuGroupTest-1.0.exe
+JPackageCreateInstallerWinPerUserInstallTest-1.0.exe
+JPackageCreateInstallerFileAssociationsTest-1.0.exe
+ECHO Make sure that installer asks to select installation location. Install to default one.
+JPackageCreateInstallerWinDirChooserTest-1.0.exe
+JPackageCreateInstallerWinRegistryNameTest-1.0.exe
+JPackageCreateInstallerWinShortcutTest-1.0.exe
+ECHO Make sure that installer shows license
+JPackageCreateInstallerLicenseTest-1.0.exe
+JPackageCreateInstallerWinUpgradeUUIDTest-1.0.exe
+JPackageCreateInstallerWinUpgradeUUIDTest-2.0.exe
+PAUSE
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/exe/uninstall.bat	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,11 @@
+"%ProgramFiles%\JPackageCreateInstallerTest\unins000.exe"
+"%ProgramFiles%\JPackageCreateInstallerWinMenuTest\unins000.exe"
+"%ProgramFiles%\JPackageCreateInstallerWinMenuGroupTest\unins000.exe"
+"%localappdata%\JPackageCreateInstallerWinPerUserInstallTest\unins000.exe"
+"%ProgramFiles%\JPackageCreateInstallerFileAssociationsTest\unins000.exe"
+"%ProgramFiles%\JPackageCreateInstallerWinDirChooserTest\unins000.exe"
+"%ProgramFiles%\JPackageCreateInstallerWinRegistryNameTest\unins000.exe"
+"%ProgramFiles%\JPackageCreateInstallerWinShortcutTest\unins000.exe"
+"%ProgramFiles%\JPackageCreateInstallerLicenseTest\unins000.exe"
+"%ProgramFiles%\JPackageCreateInstallerWinUpgradeUUIDTest\unins000.exe"
+PAUSE
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/msi/JPackageCreateInstallerFileAssociationsTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerFileAssociationsBase
+ * @requires (os.family == "windows")
+ * @modules jdk.jpackage
+ * @ignore
+ * @run main/othervm -Xmx512m JPackageCreateInstallerFileAssociationsTest
+ */
+public class JPackageCreateInstallerFileAssociationsTest {
+    private static final String TEST_NAME = "JPackageCreateInstallerFileAssociationsTest";
+    private static final String EXT = "msi";
+
+    public static void main(String[] args) throws Exception {
+        JPackageCreateInstallerFileAssociationsBase.run(TEST_NAME, EXT);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/msi/JPackageCreateInstallerLicenseTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerLicenseBase
+ * @requires (os.family == "windows")
+ * @modules jdk.jpackage
+ * @ignore
+ * @run main/othervm -Xmx512m JPackageCreateInstallerLicenseTest
+ */
+public class JPackageCreateInstallerLicenseTest {
+    private static final String TEST_NAME = "JPackageCreateInstallerLicenseTest";
+    private static final String EXT = "msi";
+
+    public static void main(String[] args) throws Exception {
+        JPackageCreateInstallerLicenseBase.run(TEST_NAME, EXT);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/msi/JPackageCreateInstallerTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerBase
+ * @requires (os.family == "windows")
+ * @modules jdk.jpackage
+ * @ignore
+ * @run main/othervm -Xmx512m JPackageCreateInstallerTest
+ */
+public class JPackageCreateInstallerTest {
+    private static final String TEST_NAME = "JPackageCreateInstallerTest";
+    private static final String EXT = "msi";
+
+    public static void main(String[] args) throws Exception {
+        JPackageCreateInstallerBase.run(TEST_NAME, EXT);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/msi/JPackageCreateInstallerWinDirChooserTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerWinDirChooserBase
+ * @requires (os.family == "windows")
+ * @modules jdk.jpackage
+ * @ignore
+ * @run main/othervm -Xmx512m JPackageCreateInstallerWinDirChooserTest
+ */
+public class JPackageCreateInstallerWinDirChooserTest {
+    private static final String TEST_NAME = "JPackageCreateInstallerWinDirChooserTest";
+    private static final String EXT = "msi";
+
+    public static void main(String[] args) throws Exception {
+        JPackageCreateInstallerWinDirChooserBase.run(TEST_NAME, EXT);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/msi/JPackageCreateInstallerWinMenuGroupTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerWinMenuGroupBase
+ * @requires (os.family == "windows")
+ * @modules jdk.jpackage
+ * @ignore
+ * @run main/othervm -Xmx512m JPackageCreateInstallerWinMenuGroupTest
+ */
+public class JPackageCreateInstallerWinMenuGroupTest {
+    private static final String TEST_NAME = "JPackageCreateInstallerWinMenuGroupTest";
+    private static final String EXT = "msi";
+
+    public static void main(String[] args) throws Exception {
+        JPackageCreateInstallerWinMenuGroupBase.run(TEST_NAME, EXT);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/msi/JPackageCreateInstallerWinMenuTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerWinMenuBase
+ * @requires (os.family == "windows")
+ * @modules jdk.jpackage
+ * @ignore
+ * @run main/othervm -Xmx512m JPackageCreateInstallerWinMenuTest
+ */
+public class JPackageCreateInstallerWinMenuTest {
+    private static final String TEST_NAME = "JPackageCreateInstallerWinMenuTest";
+    private static final String EXT = "msi";
+
+    public static void main(String[] args) throws Exception {
+        JPackageCreateInstallerWinMenuBase.run(TEST_NAME, EXT);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/msi/JPackageCreateInstallerWinPerUserInstallTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerWinPerUserInstallBase
+ * @requires (os.family == "windows")
+ * @modules jdk.jpackage
+ * @ignore
+ * @run main/othervm -Xmx512m JPackageCreateInstallerWinPerUserInstallTest
+ */
+public class JPackageCreateInstallerWinPerUserInstallTest {
+    private static final String TEST_NAME = "JPackageCreateInstallerWinPerUserInstallTest";
+    private static final String EXT = "msi";
+
+    public static void main(String[] args) throws Exception {
+        JPackageCreateInstallerWinPerUserInstallBase.run(TEST_NAME, EXT);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/msi/JPackageCreateInstallerWinRegistryNameTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerWinRegistryNameBase
+ * @requires (os.family == "windows")
+ * @modules jdk.jpackage
+ * @ignore
+ * @run main/othervm -Xmx512m JPackageCreateInstallerWinRegistryNameTest
+ */
+public class JPackageCreateInstallerWinRegistryNameTest {
+    private static final String TEST_NAME = "JPackageCreateInstallerWinRegistryNameTest";
+    private static final String EXT = "msi";
+
+    public static void main(String[] args) throws Exception {
+        JPackageCreateInstallerWinRegistryNameBase.run(TEST_NAME, EXT);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/msi/JPackageCreateInstallerWinShortcutTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerWinShortcutBase
+ * @requires (os.family == "windows")
+ * @modules jdk.jpackage
+ * @ignore
+ * @run main/othervm -Xmx512m JPackageCreateInstallerWinShortcutTest
+ */
+public class JPackageCreateInstallerWinShortcutTest {
+    private static final String TEST_NAME = "JPackageCreateInstallerWinShortcutTest";
+    private static final String EXT = "msi";
+
+    public static void main(String[] args) throws Exception {
+        JPackageCreateInstallerWinShortcutBase.run(TEST_NAME, EXT);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/msi/JPackageCreateInstallerWinUpgradeUUIDTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerWinUpgradeUUIDBase
+ * @requires (os.family == "windows")
+ * @modules jdk.jpackage
+ * @ignore
+ * @run main/othervm -Xmx512m JPackageCreateInstallerWinUpgradeUUIDTest
+ */
+public class JPackageCreateInstallerWinUpgradeUUIDTest {
+    private static final String TEST_NAME = "JPackageCreateInstallerWinUpgradeUUIDTest";
+    private static final String EXT = "msi";
+
+    public static void main(String[] args) throws Exception {
+        JPackageCreateInstallerWinUpgradeUUIDBase.run(TEST_NAME, EXT);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/msi/install.bat	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,14 @@
+JPackageCreateInstallerTest-1.0.msi
+JPackageCreateInstallerWinMenuTest-1.0.msi
+JPackageCreateInstallerWinMenuGroupTest-1.0.msi
+JPackageCreateInstallerWinPerUserInstallTest-1.0.msi
+JPackageCreateInstallerFileAssociationsTest-1.0.msi
+ECHO Make sure that installer asks to select installation location. Install to default one.
+JPackageCreateInstallerWinDirChooserTest-1.0.msi
+JPackageCreateInstallerWinRegistryNameTest-1.0.msi
+JPackageCreateInstallerWinShortcutTest-1.0.msi
+ECHO Make sure that installer shows license
+JPackageCreateInstallerLicenseTest-1.0.msi
+JPackageCreateInstallerWinUpgradeUUIDTest-1.0.msi
+JPackageCreateInstallerWinUpgradeUUIDTest-2.0.msi
+PAUSE
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/msi/uninstall.bat	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,11 @@
+MSIEXEC /uninstall JPackageCreateInstallerTest-1.0.msi
+MSIEXEC /uninstall JPackageCreateInstallerWinMenuTest-1.0.msi
+MSIEXEC /uninstall JPackageCreateInstallerWinMenuGroupTest-1.0.msi
+MSIEXEC /uninstall JPackageCreateInstallerWinPerUserInstallTest-1.0.msi
+MSIEXEC /uninstall JPackageCreateInstallerFileAssociationsTest-1.0.msi
+MSIEXEC /uninstall JPackageCreateInstallerWinDirChooserTest-1.0.msi
+MSIEXEC /uninstall JPackageCreateInstallerWinRegistryNameTest-1.0.msi
+MSIEXEC /uninstall JPackageCreateInstallerWinShortcutTest-1.0.msi
+MSIEXEC /uninstall JPackageCreateInstallerLicenseTest-1.0.msi
+MSIEXEC /uninstall JPackageCreateInstallerWinUpgradeUUIDTest-2.0.msi
+PAUSE
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/helpers/JPackageHelper.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,524 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.File;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+
+import java.util.spi.ToolProvider;
+
+public class JPackageHelper {
+
+    private static final boolean VERBOSE = false;
+    private static final String OS = System.getProperty("os.name").toLowerCase();
+    private static final String JAVA_HOME = System.getProperty("java.home");
+    public static final String TEST_SRC_ROOT;
+    public static final String TEST_SRC;
+    private static final Path BIN_DIR = Path.of(JAVA_HOME, "bin");
+    private static final Path JPACKAGE;
+    private static final Path JAVAC;
+    private static final Path JAR;
+    private static final Path JLINK;
+
+    static {
+        if (OS.startsWith("win")) {
+            JPACKAGE = BIN_DIR.resolve("jpackage.exe");
+            JAVAC = BIN_DIR.resolve("javac.exe");
+            JAR = BIN_DIR.resolve("jar.exe");
+            JLINK = BIN_DIR.resolve("jlink.exe");
+        } else {
+            JPACKAGE = BIN_DIR.resolve("jpackage");
+            JAVAC = BIN_DIR.resolve("javac");
+            JAR = BIN_DIR.resolve("jar");
+            JLINK = BIN_DIR.resolve("jlink");
+        }
+
+        // Figure out test src based on where we called
+        File testSrc = new File(System.getProperty("test.src") + File.separator + ".."
+                + File.separator + "apps");
+        if (testSrc.exists()) {
+            TEST_SRC_ROOT = System.getProperty("test.src") + File.separator + "..";
+        } else {
+            testSrc = new File(System.getProperty("test.src") + File.separator
+                    + ".." + File.separator + ".." + File.separator + "apps");
+            if (testSrc.exists()) {
+                TEST_SRC_ROOT = System.getProperty("test.src") + File.separator + ".."
+                        + File.separator + "..";
+            } else {
+                testSrc = new File(System.getProperty("test.src") + File.separator
+                        + ".." + File.separator + ".."  + File.separator + ".."
+                        + File.separator + "apps");
+                if (testSrc.exists()) {
+                    TEST_SRC_ROOT = System.getProperty("test.src") + File.separator + ".."
+                            + File.separator + ".." + File.separator + "..";
+                } else {
+                    TEST_SRC_ROOT = System.getProperty("test.src");
+                }
+            }
+        }
+
+        TEST_SRC = System.getProperty("test.src");
+    }
+
+    static final ToolProvider JPACKAGE_TOOL =
+            ToolProvider.findFirst("jpackage").orElseThrow(
+            () -> new RuntimeException("jpackage tool not found"));
+
+    public static int execute(File out, String... command) throws Exception {
+        if (VERBOSE) {
+            System.out.print("Execute command: ");
+            for (String c : command) {
+                System.out.print(c);
+                System.out.print(" ");
+            }
+            System.out.println();
+        }
+
+        ProcessBuilder builder = new ProcessBuilder(command);
+        if (out != null) {
+            builder.redirectErrorStream(true);
+            builder.redirectOutput(out);
+        }
+
+        Process process = builder.start();
+        return process.waitFor();
+    }
+
+    public static Process executeNoWait(File out, String... command) throws Exception {
+        if (VERBOSE) {
+            System.out.print("Execute command: ");
+            for (String c : command) {
+                System.out.print(c);
+                System.out.print(" ");
+            }
+            System.out.println();
+        }
+
+        ProcessBuilder builder = new ProcessBuilder(command);
+        if (out != null) {
+            builder.redirectErrorStream(true);
+            builder.redirectOutput(out);
+        }
+
+        return builder.start();
+    }
+
+    private static String[] getCommand(String... args) {
+        String[] command;
+        if (args == null) {
+            command = new String[1];
+        } else {
+            command = new String[args.length + 1];
+        }
+
+        int index = 0;
+        command[index] = JPACKAGE.toString();
+
+        if (args != null) {
+            for (String arg : args) {
+                index++;
+                command[index] = arg;
+            }
+        }
+
+        return command;
+    }
+
+    public static String executeCLI(boolean retValZero, String... args) throws Exception {
+        int retVal;
+        File outfile = new File("output.log");
+        try {
+            String[] command = getCommand(args);
+            retVal = execute(outfile, command);
+        } catch (Exception ex) {
+            if (outfile.exists()) {
+                System.err.println(Files.readString(outfile.toPath()));
+            }
+            throw ex;
+        }
+
+        String output = Files.readString(outfile.toPath());
+        if (retValZero) {
+            if (retVal != 0) {
+                System.err.println(output);
+                throw new AssertionError("jpackage exited with error: " + retVal);
+            }
+        } else {
+            if (retVal == 0) {
+                System.err.println(output);
+                throw new AssertionError("jpackage exited without error: " + retVal);
+            }
+        }
+
+        if (VERBOSE) {
+            System.out.println("output =");
+            System.out.println(output);
+        }
+
+        return output;
+    }
+
+    public static String executeToolProvider(boolean retValZero, String... args) throws Exception {
+        StringWriter writer = new StringWriter();
+        PrintWriter pw = new PrintWriter(writer);
+        int retVal = JPACKAGE_TOOL.run(pw, pw, args);
+        String output = writer.toString();
+
+        if (retValZero) {
+            if (retVal != 0) {
+                System.err.println(output);
+                throw new AssertionError("jpackage exited with error: " + retVal);
+            }
+        } else {
+            if (retVal == 0) {
+                System.err.println(output);
+                throw new AssertionError("jpackage exited without error");
+            }
+        }
+
+        if (VERBOSE) {
+            System.out.println("output =");
+            System.out.println(output);
+        }
+
+        return output;
+    }
+
+    public static boolean isWindows() {
+        return (OS.contains("win"));
+    }
+
+    public static boolean isOSX() {
+        return (OS.contains("mac"));
+    }
+
+    public static boolean isLinux() {
+        return ((OS.contains("nix") || OS.contains("nux")));
+    }
+
+    public static void createHelloImageJar() throws Exception {
+        createJar(false, "Hello", "image");
+    }
+
+    public static void createHelloImageJarWithMainClass() throws Exception {
+        createJar(true, "Hello", "image");
+    }
+
+    public static void createHelloInstallerJar() throws Exception {
+        createJar(false, "Hello", "installer");
+    }
+
+    public static void createHelloInstallerJarWithMainClass() throws Exception {
+        createJar(true, "Hello", "installer");
+    }
+
+    private static void createJar(boolean mainClassAttribute, String name,
+                                  String testType) throws Exception {
+        int retVal;
+
+        File input = new File("input");
+        if (!input.exists()) {
+            input.mkdir();
+        }
+
+        Files.copy(Path.of(TEST_SRC_ROOT + File.separator + "apps" + File.separator
+                + testType + File.separator + name + ".java"), Path.of(name + ".java"));
+
+        File javacLog = new File("javac.log");
+        try {
+            retVal = execute(javacLog, JAVAC.toString(), name + ".java");
+        } catch (Exception ex) {
+            if (javacLog.exists()) {
+                System.err.println(Files.readString(javacLog.toPath()));
+            }
+            throw ex;
+        }
+
+        if (retVal != 0) {
+            if (javacLog.exists()) {
+                System.err.println(Files.readString(javacLog.toPath()));
+            }
+            throw new AssertionError("javac exited with error: " + retVal);
+        }
+
+        File jarLog = new File("jar.log");
+        try {
+            List<String> args = new ArrayList<>();
+            args.add(JAR.toString());
+            args.add("-c");
+            args.add("-v");
+            args.add("-f");
+            args.add("input" + File.separator + name.toLowerCase() + ".jar");
+            if (mainClassAttribute) {
+                args.add("-e");
+                args.add(name);
+            }
+            args.add(name + ".class");
+            retVal = execute(jarLog, args.stream().toArray(String[]::new));
+        } catch (Exception ex) {
+            if (jarLog.exists()) {
+                System.err.println(Files.readString(jarLog.toPath()));
+            }
+            throw ex;
+        }
+
+        if (retVal != 0) {
+            if (jarLog.exists()) {
+                System.err.println(Files.readString(jarLog.toPath()));
+            }
+            throw new AssertionError("jar exited with error: " + retVal);
+        }
+    }
+
+    public static void createHelloModule() throws Exception {
+        createModule("Hello.java");
+    }
+
+    private static void createModule(String javaFile) throws Exception {
+        int retVal;
+
+        File input = new File("input");
+        if (!input.exists()) {
+            input.mkdir();
+        }
+
+        File module = new File("module" + File.separator + "com.hello");
+        if (!module.exists()) {
+            module.mkdirs();
+        }
+
+        File javacLog = new File("javac.log");
+        try {
+            List<String> args = new ArrayList<>();
+            args.add(JAVAC.toString());
+            args.add("-d");
+            args.add("module" + File.separator + "com.hello");
+            args.add(TEST_SRC_ROOT + File.separator + "apps" + File.separator + "com.hello"
+                    + File.separator + "module-info.java");
+            args.add(TEST_SRC_ROOT + File.separator + "apps" + File.separator + "com.hello"
+                    + File.separator + "com" + File.separator + "hello" + File.separator
+                    + javaFile);
+            retVal = execute(javacLog, args.stream().toArray(String[]::new));
+        } catch (Exception ex) {
+            if (javacLog.exists()) {
+                System.err.println(Files.readString(javacLog.toPath()));
+            }
+            throw ex;
+        }
+
+        if (retVal != 0) {
+            if (javacLog.exists()) {
+                System.err.println(Files.readString(javacLog.toPath()));
+            }
+            throw new AssertionError("javac exited with error: " + retVal);
+        }
+
+        File jarLog = new File("jar.log");
+        try {
+            List<String> args = new ArrayList<>();
+            args.add(JAR.toString());
+            args.add("--create");
+            args.add("--file");
+            args.add("input" + File.separator + "com.hello.jar");
+            args.add("-C");
+            args.add("module" + File.separator + "com.hello");
+            args.add(".");
+
+            retVal = execute(jarLog, args.stream().toArray(String[]::new));
+        } catch (Exception ex) {
+            if (jarLog.exists()) {
+                System.err.println(Files.readString(jarLog.toPath()));
+            }
+            throw ex;
+        }
+
+        if (retVal != 0) {
+            if (jarLog.exists()) {
+                System.err.println(Files.readString(jarLog.toPath()));
+            }
+            throw new AssertionError("jar exited with error: " + retVal);
+        }
+    }
+
+    public static void createRuntime() throws Exception {
+        int retVal;
+
+        File jlinkLog = new File("jlink.log");
+        try {
+            List<String> args = new ArrayList<>();
+            args.add(JLINK.toString());
+            args.add("--output");
+            args.add("runtime");
+            args.add("--add-modules");
+            args.add("java.base");
+            retVal = execute(jlinkLog, args.stream().toArray(String[]::new));
+        } catch (Exception ex) {
+            if (jlinkLog.exists()) {
+                System.err.println(Files.readString(jlinkLog.toPath()));
+            }
+            throw ex;
+        }
+
+        if (retVal != 0) {
+            if (jlinkLog.exists()) {
+                System.err.println(Files.readString(jlinkLog.toPath()));
+            }
+            throw new AssertionError("jlink exited with error: " + retVal);
+        }
+    }
+
+    public static String listToArgumentsMap(List<String> arguments, boolean toolProvider) {
+        if (arguments.isEmpty()) {
+            return "";
+        }
+
+        String argsStr = "";
+        for (int i = 0; i < arguments.size(); i++) {
+            String arg = arguments.get(i);
+            argsStr += quote(arg, toolProvider);
+            if ((i + 1) != arguments.size()) {
+                argsStr += " ";
+            }
+        }
+
+        if (!toolProvider && isWindows()) {
+            if (argsStr.contains(" ")) {
+                if (argsStr.contains("\"")) {
+                    argsStr = escapeQuote(argsStr, toolProvider);
+                }
+                argsStr = "\"" + argsStr + "\"";
+            }
+        }
+        return argsStr;
+    }
+
+    private static String quote(String in, boolean toolProvider) {
+        if (in == null) {
+            return null;
+        }
+
+        if (in.isEmpty()) {
+            return "";
+        }
+
+        if (!in.contains("=")) {
+            // Not a property
+            if (in.contains(" ")) {
+                in = escapeQuote(in, toolProvider);
+                return "\"" + in + "\"";
+            }
+            return in;
+        }
+
+        if (!in.contains(" ")) {
+            return in; // No need to quote
+        }
+
+        int paramIndex = in.indexOf("=");
+        if (paramIndex <= 0) {
+            return in; // Something wrong, just skip quoting
+        }
+
+        String param = in.substring(0, paramIndex);
+        String value = in.substring(paramIndex + 1);
+
+        if (value.length() == 0) {
+            return in; // No need to quote
+        }
+
+        value = escapeQuote(value, toolProvider);
+
+        return param + "=" + "\"" + value + "\"";
+    }
+
+    private static String escapeQuote(String in, boolean toolProvider) {
+        if (in == null) {
+            return null;
+        }
+
+        if (in.isEmpty()) {
+            return "";
+        }
+
+        if (in.contains("\"")) {
+            // Use code points to preserve non-ASCII chars
+            StringBuilder sb = new StringBuilder();
+            int codeLen = in.codePointCount(0, in.length());
+            for (int i = 0; i < codeLen; i++) {
+                int code = in.codePointAt(i);
+                // Note: No need to escape '\' on Linux or OS X
+                // jpackage expects us to pass arguments and properties with
+                // quotes and spaces as a map
+                // with quotes being escaped with additional \ for
+                // internal quotes.
+                // So if we want two properties below:
+                // -Djnlp.Prop1=Some "Value" 1
+                // -Djnlp.Prop2=Some Value 2
+                // jpackage will need:
+                // "-Djnlp.Prop1=\"Some \\"Value\\" 1\" -Djnlp.Prop2=\"Some Value 2\""
+                // but since we using ProcessBuilder to run jpackage we will need to escape
+                // our escape symbols as well, so we will need to pass string below to ProcessBuilder:
+                // "-Djnlp.Prop1=\\\"Some \\\\\\\"Value\\\\\\\" 1\\\" -Djnlp.Prop2=\\\"Some Value 2\\\""
+                switch (code) {
+                    case '"':
+                        // " -> \" -> \\\"
+                        if (i == 0 || in.codePointAt(i - 1) != '\\') {
+                            sb.appendCodePoint('\\');
+                            sb.appendCodePoint(code);
+                        }
+                        break;
+                    case '\\':
+                        // We need to escape already escaped symbols as well
+                        if ((i + 1) < codeLen) {
+                            int nextCode = in.codePointAt(i + 1);
+                            if (nextCode == '"') {
+                                // \" -> \\\"
+                                sb.appendCodePoint('\\');
+                                sb.appendCodePoint('\\');
+                                sb.appendCodePoint('\\');
+                                sb.appendCodePoint(nextCode);
+                            } else {
+                                sb.appendCodePoint('\\');
+                                sb.appendCodePoint(code);
+                            }
+                        } else {
+                            sb.appendCodePoint(code);
+                        }
+                        break;
+                    default:
+                        sb.appendCodePoint(code);
+                        break;
+                }
+            }
+            return sb.toString();
+        }
+
+        return in;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/helpers/JPackageInstallerHelper.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,243 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.File;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+import java.util.List;
+
+public class JPackageInstallerHelper {
+    private static final String JPACKAGE_TEST_OUTPUT = "jpackage.test.output";
+    private static final String JPACKAGE_VERIFY_INSTALL = "jpackage.verify.install";
+    private static final String JPACKAGE_VERIFY_UNINSTALL = "jpackage.verify.uninstall";
+    private static String testOutput;
+    private static final boolean isTestOutputSet;
+    private static final boolean isVerifyInstall;
+    private static final boolean isVerifyUnInstall;
+
+    static {
+        String out = System.getProperty(JPACKAGE_TEST_OUTPUT);
+        isTestOutputSet = (out != null);
+        if (isTestOutputSet) {
+            File file = new File(out);
+            if (!file.exists()) {
+                throw new AssertionError(file.getAbsolutePath() + " does not exist");
+            }
+
+            if (!file.isDirectory()) {
+                throw new AssertionError(file.getAbsolutePath() + " is not a directory");
+            }
+
+            if (!file.canWrite()) {
+                throw new AssertionError(file.getAbsolutePath() + " is not writable");
+            }
+
+            if (out.endsWith(File.separator)) {
+                out = out.substring(0, out.length() - 2);
+            }
+
+            testOutput = out;
+        }
+
+        isVerifyInstall = (System.getProperty(JPACKAGE_VERIFY_INSTALL) != null);
+        isVerifyUnInstall = (System.getProperty(JPACKAGE_VERIFY_UNINSTALL) != null);
+    }
+
+    public static boolean isTestOutputSet() {
+        return isTestOutputSet;
+    }
+
+    public static boolean isVerifyInstall() {
+        return isVerifyInstall;
+    }
+
+    public static boolean isVerifyUnInstall() {
+        return isVerifyUnInstall;
+    }
+
+    public static void copyTestResults(List<String> files) throws Exception {
+        if (!isTestOutputSet()) {
+            return;
+        }
+
+        File dest = new File(testOutput);
+        if (!dest.exists()) {
+            dest.mkdirs();
+        }
+
+        if (JPackageHelper.isWindows()) {
+            files.add(JPackagePath.getTestSrc() + File.separator + "install.bat");
+            files.add(JPackagePath.getTestSrc() + File.separator + "uninstall.bat");
+        } else {
+            files.add(JPackagePath.getTestSrc() + File.separator + "install.sh");
+            files.add(JPackagePath.getTestSrc() + File.separator + "uninstall.sh");
+        }
+
+        for (String file : files) {
+            Path source = Path.of(file);
+            Path target = Path.of(dest.toPath() + File.separator + source.getFileName());
+            Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
+        }
+    }
+
+    public static void validateApp(String app) throws Exception {
+        File outFile = new File("appOutput.txt");
+        if (outFile.exists()) {
+            outFile.delete();
+        }
+
+        int retVal = JPackageHelper.execute(outFile, app);
+        if (retVal != 0) {
+            throw new AssertionError(
+                   "Test application exited with error: " + retVal);
+        }
+
+        if (!outFile.exists()) {
+            throw new AssertionError(outFile.getAbsolutePath() + " was not created");
+        }
+
+        String output = Files.readString(outFile.toPath());
+        String[] result = output.split("\n");
+        if (result.length != 2) {
+            System.err.println(output);
+            throw new AssertionError(
+                   "Unexpected number of lines: " + result.length);
+        }
+
+        if (!result[0].trim().equals("jpackage test application")) {
+            throw new AssertionError("Unexpected result[0]: " + result[0]);
+        }
+
+        if (!result[1].trim().equals("args.length: 0")) {
+            throw new AssertionError("Unexpected result[1]: " + result[1]);
+        }
+    }
+
+    public static void validateOutput(String output) throws Exception {
+        File file = new File(output);
+        if (!file.exists()) {
+            // Try lower case in case of OS is case sensitive
+            file = new File(output.toLowerCase());
+            if (!file.exists()) {
+                throw new AssertionError("Cannot find " + file.getAbsolutePath());
+            }
+        }
+    }
+
+    public static void validateStartMenu(String menuGroup, String menu, boolean exist)
+            throws Exception {
+        String startMenuLink = JPackagePath.getWinStartMenu() +
+                File.separator + menuGroup + File.separator +
+                menu + ".lnk";
+
+        File link = new File(startMenuLink);
+        if (exist) {
+            if (!link.exists()) {
+                throw new AssertionError("Cannot find " + link.getAbsolutePath());
+            }
+        } else {
+            if (link.exists()) {
+                throw new AssertionError("Error: " + link.getAbsolutePath() + " exist");
+            }
+        }
+    }
+
+    public static void validateDesktopShortcut(String name, boolean exist)
+            throws Exception {
+        String shortcutLink = JPackagePath.getWinPublicDesktop() +
+                File.separator + name + ".lnk";
+
+        File link = new File(shortcutLink);
+        if (exist) {
+            if (!link.exists()) {
+                throw new AssertionError("Cannot find " + link.getAbsolutePath());
+            }
+        } else {
+            if (link.exists()) {
+                throw new AssertionError("Error: " + link.getAbsolutePath() + " exist");
+            }
+        }
+    }
+
+    public static void validateUserLocalStartMenu(String menuGroup, String menu, boolean exist)
+            throws Exception {
+        String startMenuLink = JPackagePath.getWinUserLocalStartMenu() +
+                File.separator + menuGroup + File.separator +
+                menu + ".lnk";
+
+        File link = new File(startMenuLink);
+        if (exist) {
+            if (!link.exists()) {
+                throw new AssertionError("Cannot find " + link.getAbsolutePath());
+            }
+        } else {
+            if (link.exists()) {
+                throw new AssertionError("Error: " + link.getAbsolutePath() + " exist");
+            }
+        }
+    }
+
+    public static void validateWinRegistry(String key, String [] values, boolean retValZero)
+            throws Exception {
+        File outFile = new File("regOutput.txt");
+        if (outFile.exists()) {
+            outFile.delete();
+        }
+
+        int retVal = JPackageHelper.execute(outFile, "reg.exe", "query", key);
+        if (retValZero) {
+            if (retVal != 0) {
+                System.out.println("validateWinRegistry() key=" + key);
+                if (outFile.exists()) {
+                    System.err.println(Files.readString(outFile.toPath()));
+                }
+                throw new AssertionError(
+                       "Reg.exe application exited with error: " + retVal);
+            }
+        } else {
+            if (retVal == 0) {
+                System.out.println("validateWinRegistry() key=" + key);
+                if (outFile.exists()) {
+                    System.err.println(Files.readString(outFile.toPath()));
+                }
+                throw new AssertionError(
+                       "Reg.exe application exited without error: " + retVal);
+            } else {
+                return; // Done
+            }
+        }
+
+        if (!outFile.exists()) {
+            throw new AssertionError(outFile.getAbsolutePath() + " was not created");
+        }
+
+        String output = Files.readString(outFile.toPath());
+        for (String value : values) {
+            if (!output.contains(value)) {
+                System.err.println(output);
+                throw new AssertionError("Cannot find in registry: " + value);
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/helpers/JPackagePath.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,284 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.File;
+
+/**
+ * Helper class which contains functions to get different system dependent paths used by tests
+ */
+public class JPackagePath {
+
+    // Path to Windows "Program Files" folder
+    // Probably better to figure this out programattically
+    private static final String WIN_PROGRAM_FILES = "C:\\Program Files";
+
+    // Path to Windows Start menu items
+    private static final String WIN_START_MENU = "C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs";
+
+    // Path to Windows public desktop location
+    private static final String WIN_PUBLIC_DESKTOP = "C:\\Users\\Public\\Desktop";
+
+    // Return path to test src adjusted to location of caller
+    public static String getTestSrcRoot() {
+        return JPackageHelper.TEST_SRC_ROOT;
+    }
+
+    // Return path to calling test
+    public static String getTestSrc() {
+        return JPackageHelper.TEST_SRC;
+    }
+
+    // Returns path to generate test application
+    public static String getApp() {
+        if (JPackageHelper.isWindows()) {
+            return "output" + File.separator + "test" + File.separator + "test.exe";
+        } else if (JPackageHelper.isOSX()) {
+            return "output" + File.separator + "test.app" + File.separator + "Contents"
+                    + File.separator + "MacOS" + File.separator + "test";
+        } else if (JPackageHelper.isLinux()) {
+            return "output" + File.separator + "test" + File.separator + "test";
+        } else {
+            throw new AssertionError("Cannot detect platform");
+        }
+    }
+
+    // Returns path to generate test application icon
+    public static String getAppIcon() {
+        if (JPackageHelper.isWindows()) {
+            return "output" + File.separator + "test" + File.separator + "test.ico";
+        } else if (JPackageHelper.isOSX()) {
+            return "output" + File.separator + "test.app" + File.separator + "Contents"
+                    + File.separator + "Resources" + File.separator + "test.icns";
+        } else if (JPackageHelper.isLinux()) {
+            return "output" + File.separator + "test" + File.separator
+                    + File.separator + "resources"+ File.separator + "test.png";
+        } else {
+            throw new AssertionError("Cannot detect platform");
+        }
+    }
+
+    // Returns path to generate test application without --name argument
+    public static String getAppNoName() {
+        if (JPackageHelper.isWindows()) {
+            return "output" + File.separator + "Hello" + File.separator + "Hello.exe";
+        } else if (JPackageHelper.isOSX()) {
+            return "output" + File.separator + "Hello.app" + File.separator + "Contents"
+                    + File.separator + "MacOS" + File.separator + "Hello";
+        } else if (JPackageHelper.isLinux()) {
+            return "output" + File.separator + "Hello" + File.separator + "Hello";
+        } else {
+            throw new AssertionError("Cannot detect platform");
+        }
+    }
+
+    // Returns path to generate secondary launcher of test application
+    public static String getAppSL() {
+        if (JPackageHelper.isWindows()) {
+            return "output" + File.separator + "test" + File.separator + "test2.exe";
+        } else if (JPackageHelper.isOSX()) {
+            return "output" + File.separator + "test.app" + File.separator + "Contents"
+                    + File.separator + "MacOS" + File.separator + "test2";
+        } else if (JPackageHelper.isLinux()) {
+            return "output" + File.separator + "test" + File.separator + "test2";
+        } else {
+            throw new AssertionError("Cannot detect platform");
+        }
+    }
+
+    // Returns path to app working directory (where test application generates its output)
+    public static String getAppWorkingDir() {
+         if (JPackageHelper.isWindows()) {
+            return "output" + File.separator + "test" + File.separator + "app";
+        } else if (JPackageHelper.isOSX()) {
+            return "output" + File.separator + "test.app" + File.separator + "Contents"
+                    + File.separator + "Java";
+        } else if (JPackageHelper.isLinux()) {
+            return "output" + File.separator + "test" + File.separator + "app";
+        } else {
+            throw new AssertionError("Cannot detect platform");
+        }
+    }
+
+    // Returns path to test application cfg file
+    public static String getAppCfg() {
+         if (JPackageHelper.isWindows()) {
+            return "output" + File.separator + "test" + File.separator + "app" + File.separator
+                    + "test.cfg";
+        } else if (JPackageHelper.isOSX()) {
+            return "output" + File.separator + "test.app" + File.separator + "Contents"
+                    + File.separator + "Java" + File.separator + "test.cfg";
+        } else if (JPackageHelper.isLinux()) {
+            return "output" + File.separator + "test" + File.separator + "app" + File.separator
+                    + "test.cfg";
+        } else {
+            throw new AssertionError("Cannot detect platform");
+        }
+    }
+
+    // Returns path to app working directory without --name (where test application generates its output)
+    public static String getAppWorkingDirNoName() {
+         if (JPackageHelper.isWindows()) {
+            return "output" + File.separator + "Hello" + File.separator + "app";
+        } else if (JPackageHelper.isOSX()) {
+            return "output" + File.separator + "Hello.app" + File.separator + "Contents"
+                    + File.separator + "Java";
+        } else if (JPackageHelper.isLinux()) {
+            return "output" + File.separator + "Hello" + File.separator + "app";
+        } else {
+            throw new AssertionError("Cannot detect platform");
+        }
+    }
+
+    // Returns path including executable to java in image runtime folder
+    public static String getRuntimeJava() {
+        if (JPackageHelper.isWindows()) {
+            return "output" + File.separator + "test" + File.separator + "runtime" + File.separator
+                    + "bin" + File.separator + "java.exe";
+        } else if (JPackageHelper.isOSX()) {
+            return "output" + File.separator + "test.app" + File.separator
+                    + "Contents" + File.separator + "PlugIns" + File.separator
+                    + "Java.runtime" + File.separator + "Contents" + File.separator
+                    + "Home" + File.separator + "bin" + File.separator + "java";
+        } else if (JPackageHelper.isLinux()) {
+            return "output" + File.separator + "test" + File.separator + "runtime" + File.separator
+                    + "bin" + File.separator + "java";
+        } else {
+            throw new AssertionError("Cannot detect platform");
+        }
+    }
+
+    // Returns output file name generate by test application
+    public static String getAppOutputFile() {
+        return "appOutput.txt";
+    }
+
+    // Returns path to bin folder in image runtime
+    public static String getRuntimeBin() {
+        if (JPackageHelper.isWindows()) {
+            return "output" + File.separator + "test" + File.separator + "runtime"
+                    + File.separator + "bin";
+        } else if (JPackageHelper.isOSX()) {
+            return "output" + File.separator + "test.app" + File.separator + "Contents"
+                    + File.separator + "PlugIns" + File.separator + "Java.runtime"
+                    + File.separator + "Contents" + File.separator + "Home" + File.separator
+                    + "bin";
+        } else if (JPackageHelper.isLinux()) {
+            return "output" + File.separator + "test" + File.separator + "runtime"
+                    + File.separator + "bin";
+        } else {
+            throw new AssertionError("Cannot detect platform");
+        }
+    }
+
+    public static String getWinProgramFiles() {
+        return WIN_PROGRAM_FILES;
+    }
+
+    public static String getWinUserLocal() {
+        return System.getProperty("user.home") + File.separator + "AppData"
+                 + File.separator + "Local";
+    }
+
+    public static String getWinStartMenu() {
+        return WIN_START_MENU;
+    }
+
+    public static String getWinPublicDesktop() {
+        return WIN_PUBLIC_DESKTOP;
+    }
+
+    public static String getWinUserLocalStartMenu() {
+        return System.getProperty("user.home") + File.separator + "AppData"
+                + File.separator + "Roaming" + File.separator + "Microsoft"
+                + File.separator + "Windows" + File.separator + "Start Menu"
+                + File.separator + "Programs";
+
+    }
+
+    public static String getWinInstalledApp(String testName) {
+        return getWinProgramFiles() + File.separator + testName + File.separator
+                + testName + ".exe";
+    }
+
+    public static String getOSXInstalledApp(String testName) {
+        return File.separator + "Applications" + File.separator + testName
+                + ".app" + File.separator + "Contents" + File.separator
+                + "MacOS" + File.separator + testName;
+    }
+
+    public static String getLinuxInstalledApp(String testName) {
+        return File.separator + "opt" + File.separator + testName +
+                File.separator + testName;
+    }
+
+    public static String getOSXInstalledApp(String subDir, String testName) {
+        return File.separator + "Applications" + File.separator + subDir
+                + File.separator + testName + ".app" + File.separator
+                + "Contents" + File.separator + "MacOS" + File.separator
+                + testName;
+    }
+
+    public static String getLinuxInstalledApp(String subDir, String testName) {
+        return File.separator + "opt" + File.separator + subDir + File.separator
+                + testName + File.separator + testName;
+    }
+
+    public static String getWinInstallFolder(String testName) {
+        return getWinProgramFiles() + File.separator + testName;
+    }
+
+    public static String getLinuxInstallFolder(String testName) {
+        return File.separator + "opt" + File.separator + testName;
+    }
+
+    public static String getLinuxInstallFolder(String subDir, String testName) {
+        if (testName == null) {
+            return File.separator + "opt" + File.separator + subDir;
+        } else {
+            return File.separator + "opt" + File.separator + subDir
+                    + File.separator + testName;
+        }
+    }
+
+    public static String getWinUserLocalInstalledApp(String testName) {
+        return getWinUserLocal() + File.separator + testName + File.separator + testName + ".exe";
+    }
+
+    public static String getWinUserLocalInstallFolder(String testName) {
+        return getWinUserLocal() + File.separator + testName;
+    }
+
+    // Returs path to test license file
+    public static String getLicenseFilePath() {
+        String path = JPackagePath.getTestSrcRoot() + File.separator + "resources"
+                + File.separator + "license.txt";
+
+        return path;
+    }
+
+    // Returns path to app folder of installed application
+    public static String getWinInstalledAppFolder(String testName) {
+        return getWinProgramFiles() + File.separator + testName + File.separator
+                + "app";
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/jdk/jpackage/internal/DeployParamsTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import jdk.jpackage.internal.Arguments;
+import jdk.jpackage.internal.DeployParams;
+import jdk.jpackage.internal.PackagerException;
+import java.io.File;
+
+/*
+ * @test
+ * @bug 8211285
+ * @summary DeployParamsTest
+ * @modules jdk.jpackage
+ * @modules jdk.jpackage/jdk.jpackage.internal
+ * @run main/othervm -Xmx512m DeployParamsTest
+ */
+public class DeployParamsTest {
+
+    private static File testRoot = null;
+
+    private static void setUp() {
+        testRoot = new File("deployParamsTest");
+        System.out.println("DeployParamsTest: " + testRoot.getAbsolutePath());
+        testRoot.mkdir();
+    }
+
+    private static void tearDown() {
+        if (testRoot != null) {
+            testRoot.delete();
+        }
+    }
+
+    private static void testValidateAppName1() throws Exception {
+        DeployParams params = getParamsAppName();
+
+        setAppName(params, "Test");
+        params.validate();
+
+        setAppName(params, "Test Name");
+        params.validate();
+
+        setAppName(params, "Test - Name !!!");
+        params.validate();
+    }
+
+    private static void testValidateAppName2() throws Exception {
+        DeployParams params = getParamsAppName();
+
+        setAppName(params, "Test\nName");
+        appName2TestHelper(params);
+
+        setAppName(params, "Test\rName");
+        appName2TestHelper(params);
+
+        setAppName(params, "TestName\\");
+        appName2TestHelper(params);
+
+        setAppName(params, "Test \" Name");
+        appName2TestHelper(params);
+    }
+
+    private static void appName2TestHelper(DeployParams params) throws Exception {
+        try {
+            params.validate();
+        } catch (PackagerException pe) {
+            if (!pe.getMessage().startsWith("Error: Invalid Application name")) {
+                throw new Exception("Unexpected PackagerException received: " + pe);
+            }
+
+            return; // Done
+        }
+
+        throw new Exception("Expecting PackagerException");
+    }
+
+    // Returns deploy params initialized to pass all validation, except for
+    // app name
+    private static DeployParams getParamsAppName() {
+        DeployParams params = new DeployParams();
+
+        params.setOutput(testRoot);
+        params.addResource(testRoot, new File(testRoot, "test.jar"));
+        params.addBundleArgument(Arguments.CLIOptions.APPCLASS.getId(), "TestClass");
+        params.addBundleArgument(Arguments.CLIOptions.MAIN_JAR.getId(), "test.jar");
+
+        return params;
+    }
+
+    private static void setAppName(DeployParams params, String appName) {
+        params.addBundleArgument(Arguments.CLIOptions.NAME.getId(), appName);
+    }
+
+    public static void main(String[] args) throws Exception {
+        setUp();
+
+        try {
+            testValidateAppName1();
+            testValidateAppName2();
+        } finally {
+            tearDown();
+        }
+    }
+
+}
Binary file test/jdk/tools/jpackage/resources/icon.icns has changed
Binary file test/jdk/tools/jpackage/resources/icon.ico has changed
Binary file test/jdk/tools/jpackage/resources/icon.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/resources/license.txt	Wed Feb 27 20:45:51 2019 -0500
@@ -0,0 +1,1 @@
+jpackage test license file (just some sample text).
--- a/test/jdk/tools/launcher/HelpFlagsTest.java	Wed Feb 27 12:19:29 2019 -0800
+++ b/test/jdk/tools/launcher/HelpFlagsTest.java	Wed Feb 27 20:45:51 2019 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 2018 SAP SE. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
@@ -156,13 +156,12 @@
         new ToolHelpSpec("jstatd",      1,   1,   1,   0,         0,    0,     1),     // -?, -h, --help
         new ToolHelpSpec("keytool",     1,   1,   1,   0,         1,    0,     1),     // none, prints help message anyways.
         new ToolHelpSpec("pack200",     1,   1,   1,   0,         1,    0,     2),     // -?, -h, --help, -help accepted but not documented.
-        new ToolHelpSpec("rmic",        0,   0,   0,   0,         0,    0,     1),     // none, pirnts help message anyways.
+        new ToolHelpSpec("rmic",        0,   0,   0,   0,         0,    0,     1),     // none, prints help message anyways.
         new ToolHelpSpec("rmid",        0,   0,   0,   0,         0,    0,     1),     // none, prints help message anyways.
         new ToolHelpSpec("rmiregistry", 0,   0,   0,   0,         0,    0,     1),     // none, prints help message anyways.
         new ToolHelpSpec("serialver",   0,   0,   0,   0,         0,    0,     1),     // none, prints help message anyways.
         new ToolHelpSpec("unpack200",   1,   1,   1,   0,         1,    0,     2),     // -?, -h, --help, -help accepted but not documented.
-        // Oracle proprietary tools:
-        new ToolHelpSpec("javapackager",0,   0,   0,   0,         1,    0,   255),     // -help accepted but not documented.
+        new ToolHelpSpec("jpackage",    0,   1,   1,   0,         0,    1,   255),     //     -h, --help,
     };
 
     // Returns true if the file is not a tool.
--- a/test/jdk/tools/launcher/VersionCheck.java	Wed Feb 27 12:19:29 2019 -0800
+++ b/test/jdk/tools/launcher/VersionCheck.java	Wed Feb 27 20:45:51 2019 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2019, Oracle and/or its affiliates. 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
@@ -62,7 +62,7 @@
         "jmc",
         "jmc.ini",
         "jweblauncher",
-        "packager",
+        "jpackage",
         "ssvagent",
         "unpack200",
     };
@@ -108,7 +108,7 @@
         "klist",
         "ktab",
         "pack200",
-        "packager",
+        "jpackage",
         "rmic",
         "rmid",
         "rmiregistry",