8232919 : If user installs msi and exe, two installations are found in Add/Remove JDK-8200758-branch
authorherrick
Mon, 04 Nov 2019 15:06:01 -0500
branchJDK-8200758-branch
changeset 58922 fbaf2e6402ad
parent 58921 d92acb18e300
child 58992 7249e95cc439
8232919 : If user installs msi and exe, two installations are found in Add/Remove Submitted-by: asemenyuk Reviewed-by: aherrick, almatvee
make/CompileJavaModules.gmk
src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinAppBundler.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/overrides.wxi
test/jdk/tools/jpackage/windows/WinResourceTest.java
--- a/make/CompileJavaModules.gmk	Mon Nov 04 14:57:27 2019 -0500
+++ b/make/CompileJavaModules.gmk	Mon Nov 04 15:06:01 2019 -0500
@@ -381,7 +381,7 @@
 ################################################################################
 
 jdk.jpackage_COPY += .gif .png .txt .spec .script .prerm .preinst .postrm .postinst .list .sh \
-    .desktop .copyright .control .plist .template .icns .scpt .entitlements .wxs .wxl .ico .bmp
+    .desktop .copyright .control .plist .template .icns .scpt .entitlements .wxs .wxl .wxi .ico .bmp
 
 jdk.jpackage_CLEAN += .properties
 
--- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinAppBundler.java	Mon Nov 04 14:57:27 2019 -0500
+++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinAppBundler.java	Mon Nov 04 15:06:01 2019 -0500
@@ -31,7 +31,6 @@
 import java.util.*;
 
 import static jdk.jpackage.internal.WindowsBundlerParam.*;
-import static jdk.jpackage.internal.WinMsiBundler.WIN_APP_IMAGE;
 
 public class WinAppBundler extends AbstractImageBundler {
 
--- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinMsiBundler.java	Mon Nov 04 14:57:27 2019 -0500
+++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinMsiBundler.java	Mon Nov 04 15:06:01 2019 -0500
@@ -27,6 +27,7 @@
 
 import java.io.*;
 import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
@@ -34,6 +35,7 @@
 import java.util.*;
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
 import javax.xml.stream.XMLOutputFactory;
 import javax.xml.stream.XMLStreamException;
 import javax.xml.stream.XMLStreamWriter;
@@ -135,12 +137,12 @@
                     (s, p) -> s
             );
 
-    public static final BundlerParamInfo<UUID> UPGRADE_UUID =
+    private static final BundlerParamInfo<String> UPGRADE_UUID =
             new WindowsBundlerParam<>(
             Arguments.CLIOptions.WIN_UPGRADE_UUID.getId(),
-            UUID.class,
-            params -> UUID.randomUUID(),
-            (s, p) -> UUID.fromString(s));
+            String.class,
+            null,
+            (s, p) -> s);
 
     @Override
     public String getName() {
@@ -186,6 +188,27 @@
         return false;
     }
 
+    private static UUID getUpgradeCode(Map<String, ? super Object> params) {
+        String upgradeCode = UPGRADE_UUID.fetchFrom(params);
+        if (upgradeCode != null) {
+            return UUID.fromString(upgradeCode);
+        }
+        return createNameUUID("UpgradeCode", params, List.of(VENDOR, APP_NAME));
+    }
+
+    private static UUID getProductCode(Map<String, ? super Object> params) {
+        return createNameUUID("ProductCode", params, List.of(VENDOR, APP_NAME,
+                VERSION));
+    }
+
+    private static UUID createNameUUID(String prefix,
+            Map<String, ? super Object> params,
+            List<StandardBundlerParam<String>> components) {
+        String key = Stream.concat(Stream.of(prefix), components.stream().map(
+                c -> c.fetchFrom(params))).collect(Collectors.joining("/"));
+        return UUID.nameUUIDFromBytes(key.getBytes(StandardCharsets.UTF_8));
+    }
+
     @Override
     public boolean validate(Map<String, ? super Object> params)
             throws ConfigException {
@@ -194,6 +217,12 @@
                 wixToolset = WixTool.toolset();
             }
 
+            try {
+                getUpgradeCode(params);
+            } catch (IllegalArgumentException ex) {
+                throw new ConfigException(ex);
+            }
+
             for (var toolInfo: wixToolset.values()) {
                 Log.verbose(MessageFormat.format(I18N.getString(
                         "message.tool-version"), toolInfo.path.getFileName(),
@@ -225,10 +254,8 @@
                     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"));
+                                I18N.getString("error.too-many-content-types-for-file-association"), i),
+                                I18N.getString("error.too-many-content-types-for-file-association.advice"));
                     }
                 }
             }
@@ -362,32 +389,27 @@
             Map<String, ? super Object> params) throws IOException {
         Map<String, String> data = new HashMap<>();
 
-        UUID productGUID = UUID.randomUUID();
+        final UUID productCode = getProductCode(params);
+        final UUID upgradeCode = getUpgradeCode(params);
 
-        Log.verbose(MessageFormat.format(
-                I18N.getString("message.generated-product-guid"),
-                productGUID.toString()));
+        data.put("JpProductCode", productCode.toString());
+        data.put("JpProductUpgradeCode", upgradeCode.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("JpProductCode", productGUID.toString());
-        data.put("JpProductUpgradeCode",
-                UPGRADE_UUID.fetchFrom(params).toString());
+        Log.verbose(MessageFormat.format(I18N.getString("message.product-code"),
+                productCode));
+        Log.verbose(MessageFormat.format(I18N.getString("message.upgrade-code"),
+                upgradeCode));
 
-        if (!UPGRADE_UUID.getIsDefaultValue()) {
-            data.put("JpAllowDowngrades", "yes");
-        }
+        data.put("JpAllowUpgrades", "yes");
 
         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));
 
-        data.put("JpConfigDir",
-                CONFIG_ROOT.fetchFrom(params).getAbsolutePath());
+        final Path configDir = CONFIG_ROOT.fetchFrom(params).toPath();
+
+        data.put("JpConfigDir", configDir.toAbsolutePath().toString());
 
         if (MSI_SYSTEM_WIDE.fetchFrom(params)) {
             data.put("JpIsSystemWide", "yes");
@@ -422,18 +444,16 @@
         }
 
         createResource("main.wxs", params)
-                .setCategory(I18N.getString("resource.wxs-file"))
-                .saveToFile(Paths.get(getConfig_ProjectFile(params)
-                .getAbsolutePath()));
+                .setCategory(I18N.getString("resource.main-wix-file"))
+                .saveToFile(configDir.resolve("main.wxs"));
 
+        createResource("overrides.wxi", params)
+                .setCategory(I18N.getString("resource.overrides-wix-file"))
+                .saveToFile(configDir.resolve("overrides.wxi"));
 
         return data;
     }
 
-    private File getConfig_ProjectFile(Map<String, ? super Object> params) {
-        return new File(CONFIG_ROOT.fetchFrom(params), "main.wxs");
-    }
-
     private File buildMSI(Map<String, ? super Object> params,
             Map<String, String> wixVars, File outdir)
             throws IOException {
--- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/MsiInstallerStrings_en.wxl	Mon Nov 04 14:57:27 2019 -0500
+++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/MsiInstallerStrings_en.wxl	Mon Nov 04 15:06:01 2019 -0500
@@ -1,4 +1,7 @@
 <?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 [INSTALLDIR] already exist. Would you like to install to that folder anyway?</String>
+  <String Id="MainFeatureTitle">Main Feature</String>
+  <String Id="DowngradeErrorMessage">A higher version of [ProductName] is already installed. Downgrades disabled. Setup will now exit.</String>
+  <String Id="DisallowUpgradeErrorMessage">A lower version of [ProductName] is already installed. Upgrades disabled. Setup will now exit.</String>
 </WixLocalization>
--- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/MsiInstallerStrings_ja.wxl	Mon Nov 04 14:57:27 2019 -0500
+++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/MsiInstallerStrings_ja.wxl	Mon Nov 04 15:06:01 2019 -0500
@@ -1,4 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
-<WixLocalization Culture="en-us" xmlns="http://schemas.microsoft.com/wix/2006/localization" Codepage="1252">
+<WixLocalization Culture="en-us" xmlns="http://schemas.microsoft.com/wix/2006/localization" Codepage="932">
   <String Id="message.install.dir.exist">The folder [INSTALLDIR] already exist. Would you like to install to that folder anyway?</String>
+  <String Id="MainFeatureTitle">Main Feature</String>
+  <String Id="DowngradeErrorMessage">A higher version of [ProductName] is already installed. Downgrades disabled. Setup will now exit.</String>
+  <String Id="DisallowUpgradeErrorMessage">A lower version of [ProductName] is already installed. Upgrades disabled. Setup will now exit.</String>
 </WixLocalization>
--- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/MsiInstallerStrings_zh_CN.wxl	Mon Nov 04 14:57:27 2019 -0500
+++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/MsiInstallerStrings_zh_CN.wxl	Mon Nov 04 15:06:01 2019 -0500
@@ -1,4 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
-<WixLocalization Culture="en-us" xmlns="http://schemas.microsoft.com/wix/2006/localization" Codepage="1252">
+<WixLocalization Culture="en-us" xmlns="http://schemas.microsoft.com/wix/2006/localization" Codepage="936">
   <String Id="message.install.dir.exist">The folder [INSTALLDIR] already exist. Would you like to install to that folder anyway?</String>
+  <String Id="MainFeatureTitle">Main Feature</String>
+  <String Id="DowngradeErrorMessage">A higher version of [ProductName] is already installed. Downgrades disabled. Setup will now exit.</String>
+  <String Id="DisallowUpgradeErrorMessage">A lower version of [ProductName] is already installed. Upgrades disabled. Setup will now exit.</String>
 </WixLocalization>
--- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources.properties	Mon Nov 04 14:57:27 2019 -0500
+++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources.properties	Mon Nov 04 15:06:01 2019 -0500
@@ -35,7 +35,8 @@
 resource.post-app-image-script=script to run after application image is populated
 resource.post-msi-script=script to run after msi file for exe installer is created
 resource.wxl-file-name=MsiInstallerStrings_en.wxl
-resource.wxs-file=Main wxs project file
+resource.main-wix-file=Main WiX project file
+resource.overrides-wix-file=Overrides WiX project file
 
 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 https://wixtoolset.org and add it to the PATH.
@@ -58,7 +59,8 @@
 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.use-wix36-features=WiX {0} detected. Enabling advanced cleanup action.
-message.generated-product-guid=Generated product GUID: {0}.
+message.product-code=MSI ProductCode: {0}.
+message.upgrade-code=MSI UpgradeCode: {0}.
 message.preparing-msi-config=Preparing MSI config: {0}.
 message.generating-msi=Generating MSI: {0}.
 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	Mon Nov 04 14:57:27 2019 -0500
+++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_ja.properties	Mon Nov 04 15:06:01 2019 -0500
@@ -35,7 +35,8 @@
 resource.post-app-image-script=script to run after application image is populated
 resource.post-msi-script=script to run after msi file for exe installer is created
 resource.wxl-file-name=MsiInstallerStrings_en.wxl
-resource.wxs-file=Main wxs project file
+resource.main-wix-file=Main WiX project file
+resource.overrides-wix-file=Overrides WiX project file
 
 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 https://wixtoolset.org and add it to the PATH.
@@ -58,7 +59,8 @@
 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.use-wix36-features=WiX {0} detected. Enabling advanced cleanup action.
-message.generated-product-guid=Generated product GUID: {0}.
+message.product-code=MSI ProductCode: {0}.
+message.upgrade-code=MSI UpgradeCode: {0}.
 message.preparing-msi-config=Preparing MSI config: {0}.
 message.generating-msi=Generating MSI: {0}.
 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	Mon Nov 04 14:57:27 2019 -0500
+++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_zh_CN.properties	Mon Nov 04 15:06:01 2019 -0500
@@ -35,7 +35,8 @@
 resource.post-app-image-script=script to run after application image is populated
 resource.post-msi-script=script to run after msi file for exe installer is created
 resource.wxl-file-name=MsiInstallerStrings_en.wxl
-resource.wxs-file=Main wxs project file
+resource.main-wix-file=Main WiX project file
+resource.overrides-wix-file=Overrides WiX project file
 
 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 https://wixtoolset.org and add it to the PATH.
@@ -58,7 +59,8 @@
 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.use-wix36-features=WiX {0} detected. Enabling advanced cleanup action.
-message.generated-product-guid=Generated product GUID: {0}.
+message.product-code=MSI ProductCode: {0}.
+message.upgrade-code=MSI UpgradeCode: {0}.
 message.preparing-msi-config=Preparing MSI config: {0}.
 message.generating-msi=Generating MSI: {0}.
 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/main.wxs	Mon Nov 04 14:57:27 2019 -0500
+++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/main.wxs	Mon Nov 04 15:06:01 2019 -0500
@@ -8,24 +8,74 @@
     <?define JpInstallScope="perUser"?>
   <?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" />
+  <?define JpProductLanguage=1033 ?>
+  <?define JpInstallerVersion=200 ?>
+  <?define JpCompressedMsi=yes ?>
+
+  <?include $(var.JpConfigDir)/overrides.wxi ?>
+
+  <Product
+    Id="$(var.JpProductCode)"
+    Name="$(var.JpAppName)"
+    Language="$(var.JpProductLanguage)"
+    Version="$(var.JpAppVersion)"
+    Manufacturer="$(var.JpAppVendor)"
+    UpgradeCode="$(var.JpProductUpgradeCode)">
+
+    <Package
+      Description="$(var.JpAppDescription)"
+      Manufacturer="$(var.JpAppVendor)"
+      InstallerVersion="$(var.JpInstallerVersion)"
+      Compressed="$(var.JpCompressedMsi)"
+      InstallScope="$(var.JpInstallScope)" Platform="x64"
+    />
+
+    <Media Id="1" Cabinet="Data.cab" EmbedCab="yes" />
 
     <?ifdef JpAllowDowngrades ?>
-    <MajorUpgrade AllowDowngrades="yes"/>
+    <?ifdef JpAllowUpgrades ?>
+    <MajorUpgrade
+      AllowDowngrades="yes"
+      Disallow="no"
+    />
+    <?endif?>
+    <?endif?>
+
+    <?ifdef JpAllowDowngrades ?>
+    <?ifndef JpAllowUpgrades ?>
+    <MajorUpgrade
+      AllowDowngrades="yes"
+      Disallow="yes"
+      DisallowUpgradeErrorMessage="!(loc.DisallowUpgradeErrorMessage)"
+    />
+    <?endif?>
+    <?endif?>
+
+    <?ifndef JpAllowDowngrades ?>
+    <?ifdef JpAllowUpgrades ?>
+    <MajorUpgrade
+      AllowDowngrades="no"
+      Disallow="no"
+      DowngradeErrorMessage="!(loc.DowngradeErrorMessage)"
+    />
+    <?endif?>
+    <?endif?>
+
+    <?ifndef JpAllowDowngrades ?>
+    <?ifndef JpAllowUpgrades ?>
+    <MajorUpgrade
+      AllowDowngrades="no"
+      Disallow="yes"
+      DowngradeErrorMessage="!(loc.DowngradeErrorMessage)"
+      DisallowUpgradeErrorMessage="!(loc.DisallowUpgradeErrorMessage)"
+    />
+    <?endif?>
     <?endif?>
 
     <!-- Standard required root -->
     <Directory Id="TARGETDIR" Name="SourceDir"/>
 
-    <Feature Id="DefaultFeature" Title="Main Feature" Level="1">
+    <Feature Id="DefaultFeature" Title="!(loc.MainFeatureTitle)" Level="1">
       <ComponentGroupRef Id="Shortcuts"/>
       <ComponentGroupRef Id="Files"/>
       <ComponentGroupRef Id="FileAssociations"/>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/overrides.wxi	Mon Nov 04 15:06:01 2019 -0500
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Stub by design -->
+<Include/>
\ No newline at end of file
--- a/test/jdk/tools/jpackage/windows/WinResourceTest.java	Mon Nov 04 14:57:27 2019 -0500
+++ b/test/jdk/tools/jpackage/windows/WinResourceTest.java	Mon Nov 04 15:06:01 2019 -0500
@@ -27,6 +27,7 @@
 import jdk.jpackage.test.PackageTest;
 import jdk.jpackage.test.PackageType;
 import jdk.jpackage.test.Annotations.Test;
+import jdk.jpackage.test.Annotations.Parameters;
 import java.util.List;
 
 /**
@@ -48,8 +49,22 @@
  */
 
 public class WinResourceTest {
+
+    public WinResourceTest(String wixSource, String expectedLogMessage) {
+         this.wixSource = wixSource;
+         this.expectedLogMessage = expectedLogMessage;
+    }
+
+    @Parameters
+    public static List<Object[]> data() {
+        return List.of(new Object[][]{
+            {"main.wxs", "Using custom package resource [Main WiX project file]"},
+            {"overrides.wxi", "Using custom package resource [Overrides WiX project file]"},
+        });
+    }
+
     @Test
-    public static void test() throws IOException {
+    public void test() throws IOException {
         new PackageTest()
         .forTypes(PackageType.WINDOWS)
         .configureHelloApp()
@@ -61,14 +76,14 @@
             cmd.setFakeRuntime().saveConsoleOutput(true);
 
             cmd.addArguments("--resource-dir", resourceDir);
-            // Create invalid main wxs file in a resource dir.
-            TKit.createTextFile(resourceDir.resolve("main.wxs"), List.of(
-                    "any string that is an invalid wxs file"));
+            // Create invalid WiX source file in a resource dir.
+            TKit.createTextFile(resourceDir.resolve(wixSource), List.of(
+                    "any string that is an invalid WiX source file"));
         })
         .addBundleVerifier((cmd, result) -> {
             // Assert jpackage picked custom main.wxs and failed as expected by
             // examining its output
-            TKit.assertTextStream("Using custom package resource [Main wxs project file]")
+            TKit.assertTextStream(expectedLogMessage)
                     .predicate(String::startsWith)
                     .apply(result.getOutput().stream());
             TKit.assertTextStream("error CNDL0104 : Not a valid source file")
@@ -77,4 +92,7 @@
         .setExpectedExitCode(1)
         .run();
     }
+
+    final String wixSource;
+    final String expectedLogMessage;
 }