8223643: Provide better defined context for custom installer steps on Windows JDK-8200758-branch
authorherrick
Thu, 13 Jun 2019 19:32:24 -0400
branchJDK-8200758-branch
changeset 57404 a477b26bf888
parent 57403 05e411fc4b77
child 57405 539d8b3f9e1e
8223643: Provide better defined context for custom installer steps on Windows Submitted-by: asemenyuk Reviewed-by: almatvee
make/CompileJavaModules.gmk
src/jdk.jpackage/share/classes/jdk/jpackage/main/CommandLine.java
src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinMsiBundler.java
src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/MsiInstallerStrings_en.wxl
src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/MsiInstallerStrings_ja.wxl
src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/MsiInstallerStrings_zh_CN.wxl
src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources.properties
src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_ja.properties
src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_zh_CN.properties
src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/main.wxs
src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/template.jre.wxs
src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/template.wxs
test/jdk/tools/jpackage/createinstaller/windows/base/JPackageCreateInstallerRuntimeBase.java
test/jdk/tools/jpackage/createinstaller/windows/exe/JPackageCreateInstallerRuntimeTest.java
test/jdk/tools/jpackage/createinstaller/windows/exe/install.bat
test/jdk/tools/jpackage/createinstaller/windows/exe/uninstall.bat
test/jdk/tools/jpackage/createinstaller/windows/msi/JPackageCreateInstallerRuntimeTest.java
test/jdk/tools/jpackage/createinstaller/windows/msi/install.bat
test/jdk/tools/jpackage/createinstaller/windows/msi/uninstall.bat
--- a/make/CompileJavaModules.gmk	Thu Jun 13 19:04:29 2019 -0400
+++ b/make/CompileJavaModules.gmk	Thu Jun 13 19:32:24 2019 -0400
@@ -381,7 +381,7 @@
 ################################################################################
 
 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
+    .desktop .copyright .control .plist .template .icns .scpt .entitlements .wxs .wxl .iss .ico .bmp
 
 jdk.jpackage_CLEAN += .properties
 
--- a/src/jdk.jpackage/share/classes/jdk/jpackage/main/CommandLine.java	Thu Jun 13 19:04:29 2019 -0400
+++ b/src/jdk.jpackage/share/classes/jdk/jpackage/main/CommandLine.java	Thu Jun 13 19:32:24 2019 -0400
@@ -49,7 +49,7 @@
  *  This code and its internal interfaces are subject to change or
  *  deletion without notice.</b>
  */
-public class CommandLine {
+class CommandLine {
     /**
      * Process Win32-style command files for the specified command line
      * arguments and return the resulting arguments. A command file argument
@@ -98,7 +98,7 @@
      * @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
+     * @throws UnmatchedQuote
      */
     public static List<String> parse(String envVariable, List<String> args)
             throws IOException, UnmatchedQuote {
@@ -125,7 +125,7 @@
      * @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
+     * @throws UnmatchedQuote
      */
     public static String[] parse(String envVariable, String[] args) throws IOException, UnmatchedQuote {
         List<String> out = parse(envVariable, Arrays.asList(args));
--- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinMsiBundler.java	Thu Jun 13 19:04:29 2019 -0400
+++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinMsiBundler.java	Thu Jun 13 19:32:24 2019 -0400
@@ -28,12 +28,66 @@
 import java.io.*;
 import java.nio.charset.Charset;
 import java.nio.file.Files;
+import java.nio.file.Paths;
 import java.text.MessageFormat;
 import java.util.*;
 import java.util.regex.Pattern;
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
 
 import static jdk.jpackage.internal.WindowsBundlerParam.*;
 
+/**
+ * WinMsiBundler
+ *
+ * Produces .msi installer from application image. Uses WiX Toolkit to build
+ * .msi installer.
+ * <p>
+ * {@link #execute} method creates a number of source files with the description
+ * of installer to be processed by WiX tools. Generated source files are stored
+ * in "config" subdirectory next to "app" subdirectory in the root work
+ * directory. The following WiX source files are generated:
+ * <ul>
+ * <li>main.wxs. Main source file with the installer description
+ * <li>bundle.wxi. Source file with application and Java run-time directory tree
+ * description. This source file is included from main.wxs
+ * <li>icons.wxi. Source file with the list of icons used by the application.
+ * This source file is included from main.wxs
+ * </ul>
+ * <p>
+ * main.wxs file is a copy of main.wxs resource from
+ * jdk.jpackage.internal.resources package. It is parametrized with the
+ * following WiX variables:
+ * <ul>
+ * <li>JpAppName. Name of the application. Set to the value of --name command
+ * line option
+ * <li>JpAppVersion. Version of the application. Set to the value of
+ * --app-version command line option
+ * <li>JpAppVendor. Vendor of the application. Set to the value of --vendor
+ * command line option
+ * <li>JpAppDescription. Description of the application. Set to the value of
+ * --description command line option
+ * <li>JpProductCode. Set to product code UUID of the application. Random value
+ * generated by jpackage every time {@link #execute} method is called
+ * <li>JpProductUpgradeCode. Set to upgrade code UUID of the application. Random
+ * value generated by jpackage every time {@link #execute} method is called if
+ * --win-upgrade-uuid command line option is not specified. Otherwise this
+ * variable is set to the value of --win-upgrade-uuid command line option
+ * <li>JpAllowDowngrades. Set to "yes" if --win-upgrade-uuid command line option
+ * was specified. Undefined otherwise
+ * <li>JpLicenseRtf. Set to the value of --license-file command line option.
+ * Undefined is --license-file command line option was not specified
+ * <li>JpInstallDirChooser. Set to "yes" if --win-dir-chooser command line
+ * option was specified. Undefined otherwise
+ * <li>JpConfigDir. Absolute path to the directory with generated WiX source
+ * files.
+ * <li>JpIsSystemWide. Set to "yes" if --win-per-user-install command line
+ * option was not specified. Undefined otherwise
+ * <li>JpWixVersion36OrNewer. Set to "yes" if WiX Toolkit v3.6 or newer is used.
+ * Undefined otherwise
+ * </ul>
+ */
 public class WinMsiBundler  extends AbstractBundler {
 
     private static final ResourceBundle I18N = ResourceBundle.getBundle(
@@ -403,6 +457,7 @@
             File destFile = new File(CONFIG_ROOT.fetchFrom(params),
                     lfile.getName());
             IOUtils.copyFile(lfile, destFile);
+            destFile.setWritable(true);
             ensureByMutationFileIsRTF(destFile);
         }
 
@@ -452,6 +507,8 @@
             throw new PackagerException("error.no-wix-tools");
         }
 
+        Map<String, String> wixVars = null;
+
         File imageDir = MSI_IMAGE_DIR.fetchFrom(params);
         try {
             imageDir.mkdirs();
@@ -464,8 +521,10 @@
                 params.put(MENU_HINT.getID(), true);
             }
 
-            if (prepareProto(params) && prepareWiXConfig(params)
-                    && prepareBasicProjectConfig(params)) {
+            prepareBasicProjectConfig(params);
+            if (prepareProto(params)) {
+                wixVars = prepareWiXConfig(params);
+
                 File configScriptSrc = getConfig_Script(params);
                 if (configScriptSrc.exists()) {
                     // we need to be running post script in the image folder
@@ -482,7 +541,7 @@
                             configScript.getAbsolutePath()));
                     IOUtils.run("wscript", configScript);
                 }
-                return buildMSI(params, outdir);
+                return buildMSI(params, wixVars, outdir);
             }
             return null;
         } catch (IOException ex) {
@@ -497,7 +556,7 @@
                 APP_NAME.fetchFrom(params) + "-post-image.wsf");
     }
 
-    private boolean prepareBasicProjectConfig(
+    private void prepareBasicProjectConfig(
         Map<String, ? super Object> params) throws IOException {
         fetchResource(getConfig_Script(params).getName(),
                 I18N.getString("resource.post-install-script"),
@@ -505,15 +564,74 @@
                 getConfig_Script(params),
                 VERBOSE.fetchFrom(params),
                 RESOURCE_DIR.fetchFrom(params));
-        return true;
     }
 
-    private String relativePath(File basedir, File file) {
+    private static String relativePath(File basedir, File file) {
         return file.getAbsolutePath().substring(
                 basedir.getAbsolutePath().length() + 1);
     }
 
-    boolean prepareMainProjectFile(
+    private void prepareIconsFile(
+            Map<String, ? super Object> params) throws IOException {
+
+        File imageRootDir = WIN_APP_IMAGE.fetchFrom(params);
+
+        List<Map<String, ? super Object>> addLaunchers =
+                ADD_LAUNCHERS.fetchFrom(params);
+
+        XMLOutputFactory xmlFactory = XMLOutputFactory.newInstance();
+        try (Writer w = new BufferedWriter(new FileWriter(new File(
+                CONFIG_ROOT.fetchFrom(params), "icons.wxi")))) {
+            XMLStreamWriter xml = xmlFactory.createXMLStreamWriter(w);
+
+            xml.writeStartDocument();
+            xml.writeStartElement("Include");
+
+            File launcher = new File(imageRootDir, WinAppBundler.getLauncherName(params));
+            if (launcher.exists()) {
+                String iconPath = launcher.getAbsolutePath().replace(".exe", ".ico");
+                if (MENU_HINT.fetchFrom(params)) {
+                    xml.writeStartElement("Icon");
+                    xml.writeAttribute("Id", "StartMenuIcon.exe");
+                    xml.writeAttribute("SourceFile", iconPath);
+                    xml.writeEndElement();
+                }
+                if (SHORTCUT_HINT.fetchFrom(params)) {
+                    xml.writeStartElement("Icon");
+                    xml.writeAttribute("Id", "DesktopIcon.exe");
+                    xml.writeAttribute("SourceFile", iconPath);
+                    xml.writeEndElement();
+                }
+            }
+
+            for (int i = 0; i < addLaunchers.size(); i++) {
+                Map<String, ? super Object> sl = addLaunchers.get(i);
+                if (SHORTCUT_HINT.fetchFrom(sl) || MENU_HINT.fetchFrom(sl)) {
+                    File addLauncher = new File(imageRootDir,
+                            WinAppBundler.getLauncherName(sl));
+                    String addLauncherPath
+                            = relativePath(imageRootDir, addLauncher);
+                    String addLauncherIconPath
+                            = addLauncherPath.replace(".exe", ".ico");
+
+                    xml.writeStartElement("Icon");
+                    xml.writeAttribute("Id", "Launcher" + i + ".exe");
+                    xml.writeAttribute("SourceFile", addLauncherIconPath);
+                    xml.writeEndElement();
+                }
+            }
+
+            xml.writeEndElement();
+            xml.writeEndDocument();
+            xml.flush();
+            xml.close();
+        } catch (XMLStreamException ex) {
+            Log.verbose(ex);
+            throw new IOException(ex);
+        }
+    }
+
+    Map<String, String> prepareMainProjectFile(
             Map<String, ? super Object> params) throws IOException {
         Map<String, String> data = new HashMap<>();
 
@@ -528,193 +646,65 @@
         // 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",
+        data.put("JpProductCode", productGUID.toString());
+        data.put("JpProductUpgradeCode",
                 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));
+        if (!UPGRADE_UUID.getIsDefaultValue()) {
+            data.put("JpAllowDowngrades", "yes");
+        }
 
-        String launcherPath = relativePath(imageRootDir, launcher);
-        data.put("APPLICATION_LAUNCHER", launcherPath);
-
-        String iconPath = launcherPath.replace(".exe", ".ico");
-
-        data.put("APPLICATION_ICON", iconPath);
+        if (CAN_USE_WIX36.fetchFrom(params)) {
+            data.put("JpWixVersion36OrNewer", "yes");
+        }
 
-        data.put("REGISTRY_ROOT", getRegistryRoot(params));
+        data.put("JpAppName", APP_NAME.fetchFrom(params));
+        data.put("JpAppDescription", DESCRIPTION.fetchFrom(params));
+        data.put("JpAppVendor", VENDOR.fetchFrom(params));
+        data.put("JpAppVersion", PRODUCT_VERSION.fetchFrom(params));
 
-        boolean canUseWix36Features = CAN_USE_WIX36.fetchFrom(params);
-        data.put("WIX36_ONLY_START",
-                canUseWix36Features ? "" : "<!--");
-        data.put("WIX36_ONLY_END",
-                canUseWix36Features ? "" : "-->");
+        data.put("JpConfigDir", CONFIG_ROOT.fetchFrom(params).getAbsolutePath());
+
+        File imageRootDir = WIN_APP_IMAGE.fetchFrom(params);
 
         if (MSI_SYSTEM_WIDE.fetchFrom(params)) {
-            data.put("INSTALL_SCOPE", "perMachine");
-        } else {
-            data.put("INSTALL_SCOPE", "perUser");
+            data.put("JpIsSystemWide", "yes");
         }
 
-        data.put("PLATFORM", "x64");
-        data.put("WIN64", "yes");
-
-        data.put("UI_BLOCK", getUIBlock(params));
+        if (LICENSE_FILE.fetchFrom(params) != null) {
+            data.put("JpLicenseRtf", LICENSE_FILE.fetchFrom(params));
+        }
 
-        // Add CA to check install dir
+        // Copy CA dll to include with installer
         if (INSTALLDIR_CHOOSER.fetchFrom(params)) {
-            data.put("CA_BLOCK", CA_BLOCK);
-            data.put("INVALID_INSTALL_DIR_DLG_BLOCK", INVALID_INSTALL_DIR_DLG_BLOCK);
-        } else {
-            data.put("CA_BLOCK", "");
-            data.put("INVALID_INSTALL_DIR_DLG_BLOCK", "");
+            data.put("JpInstallDirChooser", "yes");
+            String fname = "wixhelper.dll";
+            try (InputStream is = getResourceAsStream(fname)) {
+                Files.copy(is, Paths.get(
+                        CONFIG_ROOT.fetchFrom(params).getAbsolutePath(), fname));
+            }
         }
 
-        List<Map<String, ? super Object>> addLaunchers =
-                ADD_LAUNCHERS.fetchFrom(params);
-
-        StringBuilder addLauncherIcons = new StringBuilder();
-        for (int i = 0; i < addLaunchers.size(); i++) {
-            Map<String, ? super Object> sl = addLaunchers.get(i);
-            // <Icon Id="DesktopIcon.exe" SourceFile="APPLICATION_ICON" />
-            if (SHORTCUT_HINT.fetchFrom(sl) || MENU_HINT.fetchFrom(sl)) {
-                File addLauncher = new File(imageRootDir,
-                        WinAppBundler.getLauncherName(sl));
-                String addLauncherPath =
-                        relativePath(imageRootDir, addLauncher);
-                String addLauncherIconPath =
-                        addLauncherPath.replace(".exe", ".ico");
-
-                addLauncherIcons.append("        <Icon Id=\"Launcher");
-                addLauncherIcons.append(i);
-                addLauncherIcons.append(".exe\" SourceFile=\"");
-                addLauncherIcons.append(addLauncherIconPath);
-                addLauncherIcons.append("\" />\r\n");
+        // Copy l10n files.
+        for (String loc : Arrays.asList("en", "ja", "zh_CN")) {
+            String fname = "MsiInstallerStrings_" + loc + ".wxl";
+            try (InputStream is = getResourceAsStream(fname)) {
+                Files.copy(is, Paths.get(
+                        CONFIG_ROOT.fetchFrom(params).getAbsolutePath(), fname));
             }
         }
-        data.put("ADD_LAUNCHER_ICONS", addLauncherIcons.toString());
-
-        String wxs = StandardBundlerParam.isRuntimeInstaller(params) ?
-                MSI_PROJECT_TEMPLATE_SERVER_JRE : MSI_PROJECT_TEMPLATE;
-
-        try (Writer w = Files.newBufferedWriter(
-                getConfig_ProjectFile(params).toPath())) {
 
-            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);
+        try (InputStream is = getResourceAsStream("main.wxs")) {
+            Files.copy(is, Paths.get(
+                    getConfig_ProjectFile(params).getAbsolutePath()));
         }
-        return true;
+
+        return data;
     }
     private int id;
     private int compId;
     private final static String LAUNCHER_ID = "LauncherId";
 
-    private static final String CA_BLOCK =
-            "<Binary Id=\"CustomActionDLL\" SourceFile=\"wixhelper.dll\" />\n" +
-            "<CustomAction Id=\"CHECK_INSTALLDIR\" BinaryKey=\"CustomActionDLL\" " +
-            "DllEntry=\"CheckInstallDir\" />";
-
-    private static final String INVALID_INSTALL_DIR_DLG_BLOCK =
-            "<Dialog Id=\"InvalidInstallDir\" Width=\"300\" Height=\"85\" " +
-            "Title=\"[ProductName] Setup\" NoMinimize=\"yes\">\n" +
-            "<Control Id=\"InvalidInstallDirYes\" Type=\"PushButton\" X=\"100\" Y=\"55\" " +
-            "Width=\"50\" Height=\"15\" Default=\"no\" Cancel=\"no\" Text=\"Yes\">\n" +
-            "<Publish Event=\"NewDialog\" Value=\"VerifyReadyDlg\">1</Publish>\n" +
-            "</Control>\n" +
-            "<Control Id=\"InvalidInstallDirNo\" Type=\"PushButton\" X=\"150\" Y=\"55\" " +
-            "Width=\"50\" Height=\"15\" Default=\"yes\" Cancel=\"yes\" Text=\"No\">\n" +
-            "<Publish Event=\"NewDialog\" Value=\"InstallDirDlg\">1</Publish>\n" +
-            "</Control>\n" +
-            "<Control Id=\"Text\" Type=\"Text\" X=\"25\" Y=\"15\" Width=\"250\" Height=\"30\" " +
-            "TabSkip=\"no\">\n" +
-            "<Text>" + I18N.getString("message.install.dir.exist") + "</Text>\n" +
-            "</Control>\n" +
-            "</Dialog>";
-
-    /**
-     * 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";
-
-    private static final String CHECK_INSTALL_DLG_CTRL =
-              "     <Publish Dialog=\"InstallDirDlg\" Control=\"Next\""
-            + "              Event=\"DoAction\" Value=\"CHECK_INSTALLDIR\""
-            + " Order=\"3\">1</Publish>\n"
-            + "     <Publish Dialog=\"InstallDirDlg\" Control=\"Next\""
-            + "              Event=\"NewDialog\" Value=\"InvalidInstallDir\""
-            + " Order=\"5\">INSTALLDIR_VALID=\"0\"</Publish>\n"
-            + "     <Publish Dialog=\"InstallDirDlg\" Control=\"Next\""
-            + "              Event=\"NewDialog\" Value=\"VerifyReadyDlg\""
-            + " Order=\"5\">INSTALLDIR_VALID=\"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) throws IOException {
-        String uiBlock = ""; // UI-less element
-
-        // Copy CA dll to include with installer
-        if (INSTALLDIR_CHOOSER.fetchFrom(params)) {
-            File helper = new File(CONFIG_ROOT.fetchFrom(params), "wixhelper.dll");
-            try (InputStream is_lib = getResourceAsStream("wixhelper.dll")) {
-                Files.copy(is_lib, helper.toPath());
-            }
-        }
-
-        if (INSTALLDIR_CHOOSER.fetchFrom(params)) {
-            boolean enableTweakForExcludingLicense =
-                    (getLicenseFile(params) == null);
-            uiBlock = "     <Property Id=\"WIXUI_INSTALLDIR\""
-                    + " Value=\"APPLICATIONFOLDER\" />\n"
-                    + "     <UIRef Id=\"WixUI_InstallDir\" />\n"
-                    + (enableTweakForExcludingLicense ?
-                            TWEAK_FOR_EXCLUDING_LICENSE : "")
-                    + CHECK_INSTALL_DLG_CTRL;
-        } else if (getLicenseFile(params) != null) {
-            uiBlock = "     <UIRef Id=\"WixUI_Minimal\" />\n";
-        }
-
-        return uiBlock;
-    }
-
     private void walkFileTree(Map<String, ? super Object> params,
             File root, PrintStream out, String prefix) {
         List<File> dirs = new ArrayList<>();
@@ -936,15 +926,7 @@
         }
     }
 
-    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)
+    void prepareContentList(Map<String, ? super Object> params)
             throws FileNotFoundException {
         File f = new File(
                 CONFIG_ROOT.fetchFrom(params), MSI_PROJECT_CONTENT_FILE);
@@ -1024,14 +1006,13 @@
             for (int j = 0; j < compId; j++) {
                 out.println("    <ComponentRef Id=\"comp" + j + "\" />");
             }
-            // component is defined in the template.wsx
+            // component is defined in the main.wsx
             out.println(
                     "    <ComponentRef Id=\"CleanupMainApplicationFolder\" />");
             out.println(" </Feature>");
             out.println("</Include>");
 
         }
-        return true;
     }
 
     private File getConfig_ProjectFile(Map<String, ? super Object> params) {
@@ -1039,38 +1020,21 @@
                 APP_NAME.fetchFrom(params) + ".wxs");
     }
 
-    private String getLicenseFile(Map<String, ? super Object> params) {
-        String licenseFile = LICENSE_FILE.fetchFrom(params);
-        if (licenseFile != null) {
-            File lfile = new File(licenseFile);
-            File destFile = new File(CONFIG_ROOT.fetchFrom(params),
-                lfile.getName());
-            String filePath = destFile.getAbsolutePath();
-            if (filePath.contains(" ")) {
-                return "\"" + filePath + "\"";
-            } else {
-                return filePath;
-            }
-        }
-
-        return null;
+    private Map<String, String> prepareWiXConfig(
+            Map<String, ? super Object> params) throws IOException {
+        prepareContentList(params);
+        prepareIconsFile(params);
+        return prepareMainProjectFile(params);
     }
 
-    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)
+    private File buildMSI(Map<String, ? super Object> params,
+            Map<String, String> wixVars, File outdir)
             throws IOException {
         File tmpDir = new File(TEMP_ROOT.fetchFrom(params), "tmp");
         File candleOut = new File(
-                tmpDir, APP_NAME.fetchFrom(params) +".wixobj");
+                tmpDir, APP_NAME.fetchFrom(params) + ".wixobj");
         File msiOut = new File(
                 outdir, INSTALLER_FILE_NAME.fetchFrom(params) + ".msi");
 
@@ -1079,28 +1043,30 @@
 
         msiOut.getParentFile().mkdirs();
 
-        // run candle
-        ProcessBuilder pb = new ProcessBuilder(
+        List<String> commandLine = new ArrayList<>(Arrays.asList(
                 TOOL_CANDLE_EXECUTABLE.fetchFrom(params),
                 "-nologo",
                 getConfig_ProjectFile(params).getAbsolutePath(),
                 "-ext", "WixUtilExtension",
-                "-out", candleOut.getAbsolutePath());
+                "-out", candleOut.getAbsolutePath()));
+        for(Map.Entry<String, String> wixVar: wixVars.entrySet()) {
+            String v = "-d" + wixVar.getKey() + "=" + wixVar.getValue();
+            commandLine.add(v);
+        }
+        ProcessBuilder pb = new ProcessBuilder(commandLine);
         pb = pb.directory(WIN_APP_IMAGE.fetchFrom(params));
         IOUtils.exec(pb);
 
         Log.verbose(MessageFormat.format(I18N.getString(
                 "message.generating-msi"), msiOut.getAbsolutePath()));
 
-        boolean enableLicenseUI = (getLicenseFile(params) != null);
+        boolean enableLicenseUI = (LICENSE_FILE.fetchFrom(params) != null);
         boolean enableInstalldirUI = INSTALLDIR_CHOOSER.fetchFrom(params);
 
-        List<String> commandLine = new ArrayList<>();
+        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");
@@ -1110,9 +1076,13 @@
         commandLine.add("WixUtilExtension");
         if (enableLicenseUI || enableInstalldirUI) {
             commandLine.add("-ext");
-            commandLine.add("WixUIExtension.dll");
+            commandLine.add("WixUIExtension");
         }
 
+        commandLine.add("-loc");
+        commandLine.add(new File(CONFIG_ROOT.fetchFrom(params), I18N.getString(
+                "resource.wxl-file-name")).getAbsolutePath());
+
         // Only needed if we using CA dll, so Wix can find it
         if (enableInstalldirUI) {
             commandLine.add("-b");
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/MsiInstallerStrings_en.wxl	Thu Jun 13 19:32:24 2019 -0400
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<WixLocalization Culture="en-us" xmlns="http://schemas.microsoft.com/wix/2006/localization" Codepage="1252">
+  <String Id="message.install.dir.exist">The folder [APPLICATIONFOLDER] already exist. Would you like to install to that folder anyway?</String>
+</WixLocalization>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/MsiInstallerStrings_ja.wxl	Thu Jun 13 19:32:24 2019 -0400
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<WixLocalization Culture="en-us" xmlns="http://schemas.microsoft.com/wix/2006/localization" Codepage="1252">
+  <String Id="message.install.dir.exist">The folder [APPLICATIONFOLDER] already exist. Would you like to install to that folder anyway?</String>
+</WixLocalization>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/MsiInstallerStrings_zh_CN.wxl	Thu Jun 13 19:32:24 2019 -0400
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<WixLocalization Culture="en-us" xmlns="http://schemas.microsoft.com/wix/2006/localization" Codepage="1252">
+  <String Id="message.install.dir.exist">The folder [APPLICATIONFOLDER] already exist. Would you like to install to that folder anyway?</String>
+</WixLocalization>
--- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources.properties	Thu Jun 13 19:04:29 2019 -0400
+++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources.properties	Thu Jun 13 19:32:24 2019 -0400
@@ -37,7 +37,7 @@
 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
+resource.wxl-file-name=MsiInstallerStrings_en.wxl
 
 error.parameters-null=Parameters map is null.
 error.parameters-null.advice=Pass in a non-null parameters map.
@@ -82,6 +82,5 @@
 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}.
-message.install.dir.exist=The folder [APPLICATIONFOLDER] already exist. Whould you like to install to that folder anyway?
 message.invalid.install.dir=Warning: Invalid install directory {0}. Install directory should be a relative sub-path under the default installation location such as "Program Files". Defaulting to application name "{1}".
 
--- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_ja.properties	Thu Jun 13 19:04:29 2019 -0400
+++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_ja.properties	Thu Jun 13 19:32:24 2019 -0400
@@ -37,7 +37,7 @@
 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
+resource.wxl-file-name=MsiInstallerStrings_ja.wxl
 
 error.parameters-null=Parameters map is null.
 error.parameters-null.advice=Pass in a non-null parameters map.
@@ -82,6 +82,5 @@
 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}.
-message.install.dir.exist=The folder [APPLICATIONFOLDER] already exist. Whould you like to install to that folder anyway?
 message.invalid.install.dir=Warning: Invalid install directory {0}. Install directory should be a relative sub-path under the default installation location such as "Program Files". Defaulting to application name "{1}".
 
--- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_zh_CN.properties	Thu Jun 13 19:04:29 2019 -0400
+++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_zh_CN.properties	Thu Jun 13 19:32:24 2019 -0400
@@ -37,7 +37,7 @@
 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
+resource.wxl-file-name=MsiInstallerStrings_zh_CN.wxl
 
 error.parameters-null=Parameters map is null.
 error.parameters-null.advice=Pass in a non-null parameters map.
@@ -82,6 +82,5 @@
 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}.
-message.install.dir.exist=The folder [APPLICATIONFOLDER] already exist. Whould you like to install to that folder anyway?
 message.invalid.install.dir=Warning: Invalid install directory {0}. Install directory should be a relative sub-path under the default installation location such as "Program Files". Defaulting to application name "{1}".
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/main.wxs	Thu Jun 13 19:32:24 2019 -0400
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
+     xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">
+     
+  <?ifdef JpIsSystemWide ?>
+    <?define JpInstallScope="perMachine"?>
+    <?define JpRegistryRoot="HKLM"?>
+  <?else?>
+    <?define JpInstallScope="perUser"?>
+    <?define JpRegistryRoot="HKCU"?>
+  <?endif?>
+  
+  <Product  Id="$(var.JpProductCode)" Name="$(var.JpAppName)"
+            Language="1033" Version="$(var.JpAppVersion)"
+            Manufacturer="$(var.JpAppVendor)"
+            UpgradeCode="$(var.JpProductUpgradeCode)">
+    <Package  Description="$(var.JpAppDescription)"
+              Manufacturer="$(var.JpAppVendor)"
+              InstallerVersion="200" Compressed="yes"
+              InstallScope="$(var.JpInstallScope)" Platform="x64"/>
+    <Media Id="1" Cabinet="simple.cab" EmbedCab="yes" />
+
+    <?ifdef JpAllowDowngrades ?>
+    <MajorUpgrade AllowDowngrades="yes"/>
+    <?endif?>
+    
+        <!-- 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).
+
+             However, 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\$(var.JpAppVendor)\$(var.JpAppName)"
+                            Root="$(var.JpRegistryRoot)" Type="raw" Win64="yes"
+                            Id="APPLICATIONFOLDER_REGSEARCH" Name="Path" />
+        </Property>
+        <DirectoryRef Id="APPLICATIONFOLDER">
+            <Component Id="CleanupMainApplicationFolder" Guid="*" Win64="yes">
+                <RegistryValue Root="$(var.JpRegistryRoot)"
+                               Key="SOFTWARE\$(var.JpAppVendor)\$(var.JpAppName)"
+                               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. -->
+                  <?ifdef JpWixVersion36OrNewer ?>
+                  <util:RemoveFolderEx On="uninstall" Property="APPLICATIONFOLDER" />
+                  <?endif?>
+            </Component>
+        </DirectoryRef>
+        
+        <?include $(var.JpConfigDir)/bundle.wxi ?>
+        
+        <?ifdef JpInstallDirChooser ?>
+        <Binary Id="JpCaDll" SourceFile="wixhelper.dll"/>
+        <CustomAction Id="JpCheckInstallDir" BinaryKey="JpCaDll" DllEntry="CheckInstallDir" />
+        <?endif?>
+        
+        <UI>
+          <?ifdef JpInstallDirChooser ?>
+          <Dialog Id="JpInvalidInstallDir" Width="300" Height="85" Title="[ProductName] Setup" NoMinimize="yes">
+            <Control Id="JpInvalidInstallDirYes" Type="PushButton" X="100" Y="55" Width="50" Height="15" Default="no" Cancel="no" Text="Yes">
+              <Publish Event="NewDialog" Value="VerifyReadyDlg">1</Publish>
+            </Control>
+            <Control Id="JpInvalidInstallDirNo" Type="PushButton" X="150" Y="55" Width="50" Height="15" Default="yes" Cancel="yes" Text="No">
+              <Publish Event="NewDialog" Value="InstallDirDlg">1</Publish>
+            </Control>
+            <Control Id="Text" Type="Text" X="25" Y="15" Width="250" Height="30" TabSkip="no">
+              <Text>!(loc.message.install.dir.exist)</Text>
+            </Control>
+          </Dialog>
+          
+          <!-- 
+            Run WixUI_InstallDir dialog in the default install directory.
+          -->
+          <Property Id="WIXUI_INSTALLDIR" Value="APPLICATIONFOLDER"/>
+          <UIRef Id="WixUI_InstallDir" />
+
+          <Publish Dialog="InstallDirDlg" Control="Next" Event="DoAction" Value="JpCheckInstallDir" Order="3">1</Publish>
+          <Publish Dialog="InstallDirDlg" Control="Next" Event="NewDialog" Value="JpInvalidInstallDir" Order="5">INSTALLDIR_VALID="0"</Publish>
+          <Publish Dialog="InstallDirDlg" Control="Next" Event="NewDialog" Value="VerifyReadyDlg" Order="5">INSTALLDIR_VALID="1"</Publish>
+          
+          <?ifndef JpLicenseRtf ?>
+          <!-- 
+            No license file provided. 
+            Override the dialog sequence in built-in dialog set "WixUI_InstallDir"
+            to exclude license dialog. 
+          -->
+          <Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="InstallDirDlg" Order="2">1</Publish>
+          <Publish Dialog="InstallDirDlg" Control="Back" Event="NewDialog" Value="WelcomeDlg" Order="2">1</Publish>
+          <?endif?>
+
+          <?else?>
+          
+          <?ifdef JpLicenseRtf ?>
+          <UIRef Id="WixUI_Minimal" />
+          <?endif?>
+          
+          <?endif?>
+        </UI>
+        
+        <?ifdef JpLicenseRtf ?>
+        <WixVariable Id="WixUILicenseRtf" Value="$(var.JpLicenseRtf)"/>
+        <?endif?>
+        
+        <?include $(var.JpConfigDir)/icons.wxi ?>
+    </Product>
+</Wix>
--- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/template.jre.wxs	Thu Jun 13 19:04:29 2019 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,46 +0,0 @@
-<?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"
-                 Manufacturer="APPLICATION_VENDOR"
-                 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" Win64="WIN64"
-                            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>
--- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/template.wxs	Thu Jun 13 19:04:29 2019 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,53 +0,0 @@
-<?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"
-                 Manufacturer="APPLICATION_VENDOR"
-                 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" Win64="WIN64"
-                            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 ?>
-        CA_BLOCK
-        <UI>
-            INVALID_INSTALL_DIR_DLG_BLOCK
-            UI_BLOCK
-        </UI>
-        <Icon Id="DesktopIcon.exe" SourceFile="APPLICATION_ICON" />
-        <Icon Id="StartMenuIcon.exe" SourceFile="APPLICATION_ICON" />
-        ADD_LAUNCHER_ICONS
-    </Product>
-</Wix>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/base/JPackageCreateInstallerRuntimeBase.java	Thu Jun 13 19:32:24 2019 -0400
@@ -0,0 +1,82 @@
+/*
+ * 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.File;
+import java.util.ArrayList;
+import java.util.List;
+
+public class JPackageCreateInstallerRuntimeBase {
+
+    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 {
+        // NOP by design.
+    }
+
+    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 = "output" + File.separator + TEST_NAME + "-1.0." + EXT;
+        CMD = new String[]{
+            "create-installer",
+            "--installer-type", EXT,
+            "--output", "output",
+            "--name", TEST_NAME,
+            "--runtime-image", System.getProperty("java.home")};
+    }
+
+    public static void run(String name, String ext) throws Exception {
+        init(name, ext);
+
+        if (JPackageInstallerHelper.isVerifyInstall()) {
+            verifyInstall();
+        } else if (JPackageInstallerHelper.isVerifyUnInstall()) {
+            verifyUnInstall();
+        } else {
+            testCreateInstaller();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/createinstaller/windows/exe/JPackageCreateInstallerRuntimeTest.java	Thu Jun 13 19:32:24 2019 -0400
@@ -0,0 +1,48 @@
+/*
+ * 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.lang.invoke.MethodHandles;
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerRuntimeBase
+ * @requires (os.family == "windows")
+ * @modules jdk.jpackage
+ * @ignore
+ * @run main/othervm -Xmx512m JPackageCreateInstallerRuntimeTest
+ */
+public class JPackageCreateInstallerRuntimeTest {
+
+    private static final String TEST_NAME = MethodHandles.lookup().lookupClass().getName();
+    private static final String EXT = "exe";
+
+    public static void main(String[] args) throws Exception {
+        JPackageCreateInstallerRuntimeBase.run(TEST_NAME, EXT);
+    }
+}
--- a/test/jdk/tools/jpackage/createinstaller/windows/exe/install.bat	Thu Jun 13 19:04:29 2019 -0400
+++ b/test/jdk/tools/jpackage/createinstaller/windows/exe/install.bat	Thu Jun 13 19:32:24 2019 -0400
@@ -13,4 +13,5 @@
 JPackageCreateInstallerWinUpgradeUUIDTest-2.0.exe
 JPackageCreateInstallerInstallDirTest-1.0.exe
 JPackageCreateInstallerFileAssociationsInstallDirTest-1.0.exe
+JPackageCreateInstallerRuntimeTest-1.0.exe
 PAUSE
--- a/test/jdk/tools/jpackage/createinstaller/windows/exe/uninstall.bat	Thu Jun 13 19:04:29 2019 -0400
+++ b/test/jdk/tools/jpackage/createinstaller/windows/exe/uninstall.bat	Thu Jun 13 19:32:24 2019 -0400
@@ -10,4 +10,5 @@
 "%ProgramFiles%\JPackageCreateInstallerWinUpgradeUUIDTest\unins000.exe"
 "%ProgramFiles%\TestVendor\JPackageCreateInstallerInstallDirTestDir\unins000.exe"
 "%ProgramFiles%\TestVendor\JPackageCreateInstallerFileAssociationsInstallDirTestDir\unins000.exe"
+"%ProgramFiles%\JPackageCreateInstallerRuntimeTest\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/JPackageCreateInstallerRuntimeTest.java	Thu Jun 13 19:32:24 2019 -0400
@@ -0,0 +1,48 @@
+/*
+ * 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.lang.invoke.MethodHandles;
+
+/*
+ * @test
+ * @summary jpackage create installer test
+ * @library ../../../helpers
+ * @library ../base
+ * @build JPackageHelper
+ * @build JPackagePath
+ * @build JPackageInstallerHelper
+ * @build JPackageCreateInstallerRuntimeBase
+ * @requires (os.family == "windows")
+ * @modules jdk.jpackage
+ * @ignore
+ * @run main/othervm -Xmx512m JPackageCreateInstallerRuntimeTest
+ */
+public class JPackageCreateInstallerRuntimeTest {
+
+    private static final String TEST_NAME = MethodHandles.lookup().lookupClass().getName();
+    private static final String EXT = "msi";
+
+    public static void main(String[] args) throws Exception {
+        JPackageCreateInstallerRuntimeBase.run(TEST_NAME, EXT);
+    }
+}
--- a/test/jdk/tools/jpackage/createinstaller/windows/msi/install.bat	Thu Jun 13 19:04:29 2019 -0400
+++ b/test/jdk/tools/jpackage/createinstaller/windows/msi/install.bat	Thu Jun 13 19:32:24 2019 -0400
@@ -13,4 +13,5 @@
 JPackageCreateInstallerWinUpgradeUUIDTest-2.0.msi
 JPackageCreateInstallerInstallDirTest-1.0.msi
 JPackageCreateInstallerFileAssociationsInstallDirTest-1.0.msi
+JPackageCreateInstallerRuntimeTest-1.0.msi
 PAUSE
--- a/test/jdk/tools/jpackage/createinstaller/windows/msi/uninstall.bat	Thu Jun 13 19:04:29 2019 -0400
+++ b/test/jdk/tools/jpackage/createinstaller/windows/msi/uninstall.bat	Thu Jun 13 19:32:24 2019 -0400
@@ -10,4 +10,5 @@
 MSIEXEC /uninstall JPackageCreateInstallerWinUpgradeUUIDTest-2.0.msi
 MSIEXEC /uninstall JPackageCreateInstallerInstallDirTest-1.0.msi
 MSIEXEC /uninstall JPackageCreateInstallerFileAssociationsInstallDirTest-1.0.msi
+MSIEXEC /uninstall JPackageCreateInstallerRuntimeTest-1.0.msi
 PAUSE
\ No newline at end of file