src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinMsiBundler.java
branchJDK-8200758-branch
changeset 58922 fbaf2e6402ad
parent 58763 bc43733cd5cf
equal deleted inserted replaced
58921:d92acb18e300 58922:fbaf2e6402ad
    25 
    25 
    26 package jdk.jpackage.internal;
    26 package jdk.jpackage.internal;
    27 
    27 
    28 import java.io.*;
    28 import java.io.*;
    29 import java.nio.charset.Charset;
    29 import java.nio.charset.Charset;
       
    30 import java.nio.charset.StandardCharsets;
    30 import java.nio.file.Files;
    31 import java.nio.file.Files;
    31 import java.nio.file.Path;
    32 import java.nio.file.Path;
    32 import java.nio.file.Paths;
    33 import java.nio.file.Paths;
    33 import java.text.MessageFormat;
    34 import java.text.MessageFormat;
    34 import java.util.*;
    35 import java.util.*;
    35 import java.util.regex.Pattern;
    36 import java.util.regex.Pattern;
    36 import java.util.stream.Collectors;
    37 import java.util.stream.Collectors;
       
    38 import java.util.stream.Stream;
    37 import javax.xml.stream.XMLOutputFactory;
    39 import javax.xml.stream.XMLOutputFactory;
    38 import javax.xml.stream.XMLStreamException;
    40 import javax.xml.stream.XMLStreamException;
    39 import javax.xml.stream.XMLStreamWriter;
    41 import javax.xml.stream.XMLStreamWriter;
    40 import static jdk.jpackage.internal.OverridableResource.createResource;
    42 import static jdk.jpackage.internal.OverridableResource.createResource;
    41 import static jdk.jpackage.internal.StandardBundlerParam.*;
    43 import static jdk.jpackage.internal.StandardBundlerParam.*;
   133                     String.class,
   135                     String.class,
   134                     VERSION::fetchFrom,
   136                     VERSION::fetchFrom,
   135                     (s, p) -> s
   137                     (s, p) -> s
   136             );
   138             );
   137 
   139 
   138     public static final BundlerParamInfo<UUID> UPGRADE_UUID =
   140     private static final BundlerParamInfo<String> UPGRADE_UUID =
   139             new WindowsBundlerParam<>(
   141             new WindowsBundlerParam<>(
   140             Arguments.CLIOptions.WIN_UPGRADE_UUID.getId(),
   142             Arguments.CLIOptions.WIN_UPGRADE_UUID.getId(),
   141             UUID.class,
   143             String.class,
   142             params -> UUID.randomUUID(),
   144             null,
   143             (s, p) -> UUID.fromString(s));
   145             (s, p) -> s);
   144 
   146 
   145     @Override
   147     @Override
   146     public String getName() {
   148     public String getName() {
   147         return I18N.getString("msi.bundler.name");
   149         return I18N.getString("msi.bundler.name");
   148     }
   150     }
   184     @Override
   186     @Override
   185     public boolean isDefault() {
   187     public boolean isDefault() {
   186         return false;
   188         return false;
   187     }
   189     }
   188 
   190 
       
   191     private static UUID getUpgradeCode(Map<String, ? super Object> params) {
       
   192         String upgradeCode = UPGRADE_UUID.fetchFrom(params);
       
   193         if (upgradeCode != null) {
       
   194             return UUID.fromString(upgradeCode);
       
   195         }
       
   196         return createNameUUID("UpgradeCode", params, List.of(VENDOR, APP_NAME));
       
   197     }
       
   198 
       
   199     private static UUID getProductCode(Map<String, ? super Object> params) {
       
   200         return createNameUUID("ProductCode", params, List.of(VENDOR, APP_NAME,
       
   201                 VERSION));
       
   202     }
       
   203 
       
   204     private static UUID createNameUUID(String prefix,
       
   205             Map<String, ? super Object> params,
       
   206             List<StandardBundlerParam<String>> components) {
       
   207         String key = Stream.concat(Stream.of(prefix), components.stream().map(
       
   208                 c -> c.fetchFrom(params))).collect(Collectors.joining("/"));
       
   209         return UUID.nameUUIDFromBytes(key.getBytes(StandardCharsets.UTF_8));
       
   210     }
       
   211 
   189     @Override
   212     @Override
   190     public boolean validate(Map<String, ? super Object> params)
   213     public boolean validate(Map<String, ? super Object> params)
   191             throws ConfigException {
   214             throws ConfigException {
   192         try {
   215         try {
   193             if (wixToolset == null) {
   216             if (wixToolset == null) {
   194                 wixToolset = WixTool.toolset();
   217                 wixToolset = WixTool.toolset();
       
   218             }
       
   219 
       
   220             try {
       
   221                 getUpgradeCode(params);
       
   222             } catch (IllegalArgumentException ex) {
       
   223                 throw new ConfigException(ex);
   195             }
   224             }
   196 
   225 
   197             for (var toolInfo: wixToolset.values()) {
   226             for (var toolInfo: wixToolset.values()) {
   198                 Log.verbose(MessageFormat.format(I18N.getString(
   227                 Log.verbose(MessageFormat.format(I18N.getString(
   199                         "message.tool-version"), toolInfo.path.getFileName(),
   228                         "message.tool-version"), toolInfo.path.getFileName(),
   223                 for (int i = 0; i < associations.size(); i++) {
   252                 for (int i = 0; i < associations.size(); i++) {
   224                     Map<String, ? super Object> assoc = associations.get(i);
   253                     Map<String, ? super Object> assoc = associations.get(i);
   225                     List<String> mimes = FA_CONTENT_TYPE.fetchFrom(assoc);
   254                     List<String> mimes = FA_CONTENT_TYPE.fetchFrom(assoc);
   226                     if (mimes.size() > 1) {
   255                     if (mimes.size() > 1) {
   227                         throw new ConfigException(MessageFormat.format(
   256                         throw new ConfigException(MessageFormat.format(
   228                                 I18N.getString("error.too-many-content-"
   257                                 I18N.getString("error.too-many-content-types-for-file-association"), i),
   229                                 + "types-for-file-association"), i),
   258                                 I18N.getString("error.too-many-content-types-for-file-association.advice"));
   230                                 I18N.getString("error.too-many-content-"
       
   231                                 + "types-for-file-association.advice"));
       
   232                     }
   259                     }
   233                 }
   260                 }
   234             }
   261             }
   235 
   262 
   236             return true;
   263             return true;
   360 
   387 
   361     Map<String, String> prepareMainProjectFile(
   388     Map<String, String> prepareMainProjectFile(
   362             Map<String, ? super Object> params) throws IOException {
   389             Map<String, ? super Object> params) throws IOException {
   363         Map<String, String> data = new HashMap<>();
   390         Map<String, String> data = new HashMap<>();
   364 
   391 
   365         UUID productGUID = UUID.randomUUID();
   392         final UUID productCode = getProductCode(params);
   366 
   393         final UUID upgradeCode = getUpgradeCode(params);
   367         Log.verbose(MessageFormat.format(
   394 
   368                 I18N.getString("message.generated-product-guid"),
   395         data.put("JpProductCode", productCode.toString());
   369                 productGUID.toString()));
   396         data.put("JpProductUpgradeCode", upgradeCode.toString());
   370 
   397 
   371         // we use random GUID for product itself but
   398         Log.verbose(MessageFormat.format(I18N.getString("message.product-code"),
   372         // user provided for upgrade guid
   399                 productCode));
   373         // Upgrade guid is important to decide whether it is an upgrade of
   400         Log.verbose(MessageFormat.format(I18N.getString("message.upgrade-code"),
   374         // installed app.  I.e. we need it to be the same for
   401                 upgradeCode));
   375         // 2 different versions of app if possible
   402 
   376         data.put("JpProductCode", productGUID.toString());
   403         data.put("JpAllowUpgrades", "yes");
   377         data.put("JpProductUpgradeCode",
       
   378                 UPGRADE_UUID.fetchFrom(params).toString());
       
   379 
       
   380         if (!UPGRADE_UUID.getIsDefaultValue()) {
       
   381             data.put("JpAllowDowngrades", "yes");
       
   382         }
       
   383 
   404 
   384         data.put("JpAppName", APP_NAME.fetchFrom(params));
   405         data.put("JpAppName", APP_NAME.fetchFrom(params));
   385         data.put("JpAppDescription", DESCRIPTION.fetchFrom(params));
   406         data.put("JpAppDescription", DESCRIPTION.fetchFrom(params));
   386         data.put("JpAppVendor", VENDOR.fetchFrom(params));
   407         data.put("JpAppVendor", VENDOR.fetchFrom(params));
   387         data.put("JpAppVersion", PRODUCT_VERSION.fetchFrom(params));
   408         data.put("JpAppVersion", PRODUCT_VERSION.fetchFrom(params));
   388 
   409 
   389         data.put("JpConfigDir",
   410         final Path configDir = CONFIG_ROOT.fetchFrom(params).toPath();
   390                 CONFIG_ROOT.fetchFrom(params).getAbsolutePath());
   411 
       
   412         data.put("JpConfigDir", configDir.toAbsolutePath().toString());
   391 
   413 
   392         if (MSI_SYSTEM_WIDE.fetchFrom(params)) {
   414         if (MSI_SYSTEM_WIDE.fetchFrom(params)) {
   393             data.put("JpIsSystemWide", "yes");
   415             data.put("JpIsSystemWide", "yes");
   394         }
   416         }
   395 
   417 
   420                         fname));
   442                         fname));
   421             }
   443             }
   422         }
   444         }
   423 
   445 
   424         createResource("main.wxs", params)
   446         createResource("main.wxs", params)
   425                 .setCategory(I18N.getString("resource.wxs-file"))
   447                 .setCategory(I18N.getString("resource.main-wix-file"))
   426                 .saveToFile(Paths.get(getConfig_ProjectFile(params)
   448                 .saveToFile(configDir.resolve("main.wxs"));
   427                 .getAbsolutePath()));
   449 
   428 
   450         createResource("overrides.wxi", params)
       
   451                 .setCategory(I18N.getString("resource.overrides-wix-file"))
       
   452                 .saveToFile(configDir.resolve("overrides.wxi"));
   429 
   453 
   430         return data;
   454         return data;
   431     }
       
   432 
       
   433     private File getConfig_ProjectFile(Map<String, ? super Object> params) {
       
   434         return new File(CONFIG_ROOT.fetchFrom(params), "main.wxs");
       
   435     }
   455     }
   436 
   456 
   437     private File buildMSI(Map<String, ? super Object> params,
   457     private File buildMSI(Map<String, ? super Object> params,
   438             Map<String, String> wixVars, File outdir)
   458             Map<String, String> wixVars, File outdir)
   439             throws IOException {
   459             throws IOException {