src/jdk.incubator.jpackage/macosx/classes/jdk/incubator/jpackage/internal/MacPkgBundler.java
branchJDK-8200758-branch
changeset 58994 b09ba68c6a19
parent 58762 0fe62353385b
equal deleted inserted replaced
58993:b5e1baa9d2c3 58994:b09ba68c6a19
       
     1 /*
       
     2  * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Oracle designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Oracle in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    22  * or visit www.oracle.com if you need additional information or have any
       
    23  * questions.
       
    24  */
       
    25 
       
    26 package jdk.incubator.jpackage.internal;
       
    27 
       
    28 import java.io.*;
       
    29 import java.net.URI;
       
    30 import java.net.URISyntaxException;
       
    31 import java.nio.file.Files;
       
    32 import java.nio.file.Path;
       
    33 import java.text.MessageFormat;
       
    34 import java.util.*;
       
    35 
       
    36 import static jdk.incubator.jpackage.internal.StandardBundlerParam.*;
       
    37 import static jdk.incubator.jpackage.internal.MacBaseInstallerBundler.SIGNING_KEYCHAIN;
       
    38 import static jdk.incubator.jpackage.internal.MacBaseInstallerBundler.SIGNING_KEY_USER;
       
    39 import static jdk.incubator.jpackage.internal.MacAppImageBuilder.MAC_CF_BUNDLE_IDENTIFIER;
       
    40 import static jdk.incubator.jpackage.internal.OverridableResource.createResource;
       
    41 
       
    42 public class MacPkgBundler extends MacBaseInstallerBundler {
       
    43 
       
    44     private static final ResourceBundle I18N = ResourceBundle.getBundle(
       
    45             "jdk.incubator.jpackage.internal.resources.MacResources");
       
    46 
       
    47     private static final String DEFAULT_BACKGROUND_IMAGE = "background_pkg.png";
       
    48 
       
    49     private static final String TEMPLATE_PREINSTALL_SCRIPT =
       
    50             "preinstall.template";
       
    51     private static final String TEMPLATE_POSTINSTALL_SCRIPT =
       
    52             "postinstall.template";
       
    53 
       
    54     private static final BundlerParamInfo<File> PACKAGES_ROOT =
       
    55             new StandardBundlerParam<>(
       
    56             "mac.pkg.packagesRoot",
       
    57             File.class,
       
    58             params -> {
       
    59                 File packagesRoot =
       
    60                         new File(TEMP_ROOT.fetchFrom(params), "packages");
       
    61                 packagesRoot.mkdirs();
       
    62                 return packagesRoot;
       
    63             },
       
    64             (s, p) -> new File(s));
       
    65 
       
    66 
       
    67     protected final BundlerParamInfo<File> SCRIPTS_DIR =
       
    68             new StandardBundlerParam<>(
       
    69             "mac.pkg.scriptsDir",
       
    70             File.class,
       
    71             params -> {
       
    72                 File scriptsDir =
       
    73                         new File(CONFIG_ROOT.fetchFrom(params), "scripts");
       
    74                 scriptsDir.mkdirs();
       
    75                 return scriptsDir;
       
    76             },
       
    77             (s, p) -> new File(s));
       
    78 
       
    79     public static final
       
    80             BundlerParamInfo<String> DEVELOPER_ID_INSTALLER_SIGNING_KEY =
       
    81             new StandardBundlerParam<>(
       
    82             "mac.signing-key-developer-id-installer",
       
    83             String.class,
       
    84             params -> {
       
    85                     String result = MacBaseInstallerBundler.findKey(
       
    86                             "Developer ID Installer: "
       
    87                             + SIGNING_KEY_USER.fetchFrom(params),
       
    88                             SIGNING_KEYCHAIN.fetchFrom(params),
       
    89                             VERBOSE.fetchFrom(params));
       
    90                     if (result != null) {
       
    91                         MacCertificate certificate = new MacCertificate(result);
       
    92 
       
    93                         if (!certificate.isValid()) {
       
    94                             Log.error(MessageFormat.format(
       
    95                                     I18N.getString("error.certificate.expired"),
       
    96                                     result));
       
    97                         }
       
    98                     }
       
    99 
       
   100                     return result;
       
   101                 },
       
   102             (s, p) -> s);
       
   103 
       
   104     public static final BundlerParamInfo<String> MAC_INSTALL_DIR =
       
   105             new StandardBundlerParam<>(
       
   106             "mac-install-dir",
       
   107             String.class,
       
   108              params -> {
       
   109                  String dir = INSTALL_DIR.fetchFrom(params);
       
   110                  return (dir != null) ? dir : "/Applications";
       
   111              },
       
   112             (s, p) -> s
       
   113     );
       
   114 
       
   115     public static final BundlerParamInfo<String> INSTALLER_SUFFIX =
       
   116             new StandardBundlerParam<> (
       
   117             "mac.pkg.installerName.suffix",
       
   118             String.class,
       
   119             params -> "",
       
   120             (s, p) -> s);
       
   121 
       
   122     public File bundle(Map<String, ? super Object> params,
       
   123             File outdir) throws PackagerException {
       
   124         Log.verbose(MessageFormat.format(I18N.getString("message.building-pkg"),
       
   125                 APP_NAME.fetchFrom(params)));
       
   126 
       
   127         IOUtils.writableOutputDir(outdir.toPath());
       
   128 
       
   129         try {
       
   130             File appImageDir = prepareAppBundle(params);
       
   131 
       
   132             if (appImageDir != null && prepareConfigFiles(params)) {
       
   133 
       
   134                 File configScript = getConfig_Script(params);
       
   135                 if (configScript.exists()) {
       
   136                     Log.verbose(MessageFormat.format(I18N.getString(
       
   137                             "message.running-script"),
       
   138                             configScript.getAbsolutePath()));
       
   139                     IOUtils.run("bash", configScript);
       
   140                 }
       
   141 
       
   142                 return createPKG(params, outdir, appImageDir);
       
   143             }
       
   144             return null;
       
   145         } catch (IOException ex) {
       
   146             Log.verbose(ex);
       
   147             throw new PackagerException(ex);
       
   148         }
       
   149     }
       
   150 
       
   151     private File getPackages_AppPackage(Map<String, ? super Object> params) {
       
   152         return new File(PACKAGES_ROOT.fetchFrom(params),
       
   153                 APP_NAME.fetchFrom(params) + "-app.pkg");
       
   154     }
       
   155 
       
   156     private File getConfig_DistributionXMLFile(
       
   157             Map<String, ? super Object> params) {
       
   158         return new File(CONFIG_ROOT.fetchFrom(params), "distribution.dist");
       
   159     }
       
   160 
       
   161     private File getConfig_BackgroundImage(Map<String, ? super Object> params) {
       
   162         return new File(CONFIG_ROOT.fetchFrom(params),
       
   163                 APP_NAME.fetchFrom(params) + "-background.png");
       
   164     }
       
   165 
       
   166     private File getConfig_BackgroundImageDarkAqua(Map<String, ? super Object> params) {
       
   167         return new File(CONFIG_ROOT.fetchFrom(params),
       
   168                 APP_NAME.fetchFrom(params) + "-background-darkAqua.png");
       
   169     }
       
   170 
       
   171     private File getScripts_PreinstallFile(Map<String, ? super Object> params) {
       
   172         return new File(SCRIPTS_DIR.fetchFrom(params), "preinstall");
       
   173     }
       
   174 
       
   175     private File getScripts_PostinstallFile(
       
   176             Map<String, ? super Object> params) {
       
   177         return new File(SCRIPTS_DIR.fetchFrom(params), "postinstall");
       
   178     }
       
   179 
       
   180     private String getAppIdentifier(Map<String, ? super Object> params) {
       
   181         return MAC_CF_BUNDLE_IDENTIFIER.fetchFrom(params);
       
   182     }
       
   183 
       
   184     private void preparePackageScripts(Map<String, ? super Object> params)
       
   185             throws IOException {
       
   186         Log.verbose(I18N.getString("message.preparing-scripts"));
       
   187 
       
   188         Map<String, String> data = new HashMap<>();
       
   189 
       
   190         Path appLocation = Path.of(MAC_INSTALL_DIR.fetchFrom(params),
       
   191                          APP_NAME.fetchFrom(params) + ".app", "Contents", "app");
       
   192 
       
   193         data.put("INSTALL_LOCATION", MAC_INSTALL_DIR.fetchFrom(params));
       
   194         data.put("APP_LOCATION", appLocation.toString());
       
   195 
       
   196         createResource(TEMPLATE_PREINSTALL_SCRIPT, params)
       
   197                 .setCategory(I18N.getString("resource.pkg-preinstall-script"))
       
   198                 .setSubstitutionData(data)
       
   199                 .saveToFile(getScripts_PreinstallFile(params));
       
   200         getScripts_PreinstallFile(params).setExecutable(true, false);
       
   201 
       
   202         createResource(TEMPLATE_POSTINSTALL_SCRIPT, params)
       
   203                 .setCategory(I18N.getString("resource.pkg-postinstall-script"))
       
   204                 .setSubstitutionData(data)
       
   205                 .saveToFile(getScripts_PostinstallFile(params));
       
   206         getScripts_PostinstallFile(params).setExecutable(true, false);
       
   207     }
       
   208 
       
   209     private static String URLEncoding(String pkgName) throws URISyntaxException {
       
   210         URI uri = new URI(null, null, pkgName, null);
       
   211         return uri.toASCIIString();
       
   212     }
       
   213 
       
   214     private void prepareDistributionXMLFile(Map<String, ? super Object> params)
       
   215             throws IOException {
       
   216         File f = getConfig_DistributionXMLFile(params);
       
   217 
       
   218         Log.verbose(MessageFormat.format(I18N.getString(
       
   219                 "message.preparing-distribution-dist"), f.getAbsolutePath()));
       
   220 
       
   221         IOUtils.createXml(f.toPath(), xml -> {
       
   222             xml.writeStartElement("installer-gui-script");
       
   223             xml.writeAttribute("minSpecVersion", "1");
       
   224 
       
   225             xml.writeStartElement("title");
       
   226             xml.writeCharacters(APP_NAME.fetchFrom(params));
       
   227             xml.writeEndElement();
       
   228 
       
   229             xml.writeStartElement("background");
       
   230             xml.writeAttribute("file", getConfig_BackgroundImage(params).getName());
       
   231             xml.writeAttribute("mime-type", "image/png");
       
   232             xml.writeAttribute("alignment", "bottomleft");
       
   233             xml.writeAttribute("scaling", "none");
       
   234             xml.writeEndElement();
       
   235 
       
   236             xml.writeStartElement("background-darkAqua");
       
   237             xml.writeAttribute("file", getConfig_BackgroundImageDarkAqua(params).getName());
       
   238             xml.writeAttribute("mime-type", "image/png");
       
   239             xml.writeAttribute("alignment", "bottomleft");
       
   240             xml.writeAttribute("scaling", "none");
       
   241             xml.writeEndElement();
       
   242 
       
   243             String licFileStr = LICENSE_FILE.fetchFrom(params);
       
   244             if (licFileStr != null) {
       
   245                 File licFile = new File(licFileStr);
       
   246                 xml.writeStartElement("license");
       
   247                 xml.writeAttribute("file", licFile.getAbsolutePath());
       
   248                 xml.writeAttribute("mime-type", "text/rtf");
       
   249                 xml.writeEndElement();
       
   250             }
       
   251 
       
   252             /*
       
   253              * Note that the content of the distribution file
       
   254              * below is generated by productbuild --synthesize
       
   255              */
       
   256             String appId = getAppIdentifier(params);
       
   257 
       
   258             xml.writeStartElement("pkg-ref");
       
   259             xml.writeAttribute("id", appId);
       
   260             xml.writeEndElement(); // </pkg-ref>
       
   261             xml.writeStartElement("options");
       
   262             xml.writeAttribute("customize", "never");
       
   263             xml.writeAttribute("require-scripts", "false");
       
   264             xml.writeEndElement(); // </options>
       
   265             xml.writeStartElement("choices-outline");
       
   266             xml.writeStartElement("line");
       
   267             xml.writeAttribute("choice", "default");
       
   268             xml.writeStartElement("line");
       
   269             xml.writeAttribute("choice", appId);
       
   270             xml.writeEndElement(); // </line>
       
   271             xml.writeEndElement(); // </line>
       
   272             xml.writeEndElement(); // </choices-outline>
       
   273             xml.writeStartElement("choice");
       
   274             xml.writeAttribute("id", "default");
       
   275             xml.writeEndElement(); // </choice>
       
   276             xml.writeStartElement("choice");
       
   277             xml.writeAttribute("id", appId);
       
   278             xml.writeAttribute("visible", "false");
       
   279             xml.writeStartElement("pkg-ref");
       
   280             xml.writeAttribute("id", appId);
       
   281             xml.writeEndElement(); // </pkg-ref>
       
   282             xml.writeEndElement(); // </choice>
       
   283             xml.writeStartElement("pkg-ref");
       
   284             xml.writeAttribute("id", appId);
       
   285             xml.writeAttribute("version", VERSION.fetchFrom(params));
       
   286             xml.writeAttribute("onConclusion", "none");
       
   287             try {
       
   288                 xml.writeCharacters(URLEncoding(
       
   289                         getPackages_AppPackage(params).getName()));
       
   290             } catch (URISyntaxException ex) {
       
   291                 throw new IOException(ex);
       
   292             }
       
   293             xml.writeEndElement(); // </pkg-ref>
       
   294 
       
   295             xml.writeEndElement(); // </installer-gui-script>
       
   296         });
       
   297     }
       
   298 
       
   299     private boolean prepareConfigFiles(Map<String, ? super Object> params)
       
   300             throws IOException {
       
   301 
       
   302         createResource(DEFAULT_BACKGROUND_IMAGE, params)
       
   303                 .setCategory(I18N.getString("resource.pkg-background-image"))
       
   304                 .saveToFile(getConfig_BackgroundImage(params));
       
   305 
       
   306         createResource(DEFAULT_BACKGROUND_IMAGE, params)
       
   307                 .setCategory(I18N.getString("resource.pkg-background-image"))
       
   308                 .saveToFile(getConfig_BackgroundImageDarkAqua(params));
       
   309 
       
   310         prepareDistributionXMLFile(params);
       
   311 
       
   312         createResource(null, params)
       
   313                 .setCategory(I18N.getString("resource.post-install-script"))
       
   314                 .saveToFile(getConfig_Script(params));
       
   315 
       
   316         return true;
       
   317     }
       
   318 
       
   319     // name of post-image script
       
   320     private File getConfig_Script(Map<String, ? super Object> params) {
       
   321         return new File(CONFIG_ROOT.fetchFrom(params),
       
   322                 APP_NAME.fetchFrom(params) + "-post-image.sh");
       
   323     }
       
   324 
       
   325     private void patchCPLFile(File cpl) throws IOException {
       
   326         String cplData = Files.readString(cpl.toPath());
       
   327         String[] lines = cplData.split("\n");
       
   328         try (PrintWriter out = new PrintWriter(Files.newBufferedWriter(
       
   329                 cpl.toPath()))) {
       
   330             int skip = 0;
       
   331             // Used to skip Java.runtime bundle, since
       
   332             // pkgbuild with --root will find two bundles app and Java runtime.
       
   333             // We cannot generate component proprty list when using
       
   334             // --component argument.
       
   335             for (int i = 0; i < lines.length; i++) {
       
   336                 if (lines[i].trim().equals("<key>BundleIsRelocatable</key>")) {
       
   337                     out.println(lines[i]);
       
   338                     out.println("<false/>");
       
   339                     i++;
       
   340                 } else if (lines[i].trim().equals("<key>ChildBundles</key>")) {
       
   341                     ++skip;
       
   342                 } else if ((skip > 0) && lines[i].trim().equals("</array>")) {
       
   343                     --skip;
       
   344                 } else {
       
   345                     if (skip == 0) {
       
   346                         out.println(lines[i]);
       
   347                     }
       
   348                 }
       
   349             }
       
   350         }
       
   351     }
       
   352 
       
   353     // pkgbuild includes all components from "--root" and subfolders,
       
   354     // so if we have app image in folder which contains other images, then they
       
   355     // will be included as well. It does have "--filter" option which use regex
       
   356     // to exclude files/folder, but it will overwrite default one which excludes
       
   357     // based on doc "any .svn or CVS directories, and any .DS_Store files".
       
   358     // So easy aproach will be to copy user provided app-image into temp folder
       
   359     // if root path contains other files.
       
   360     private String getRoot(Map<String, ? super Object> params,
       
   361             File appLocation) throws IOException {
       
   362         String root = appLocation.getParent() == null ?
       
   363                 "." : appLocation.getParent();
       
   364         File rootDir = new File(root);
       
   365         File[] list = rootDir.listFiles();
       
   366         if (list != null) { // Should not happend
       
   367             // We should only have app image and/or .DS_Store
       
   368             if (list.length == 1) {
       
   369                 return root;
       
   370             } else if (list.length == 2) {
       
   371                 // Check case with app image and .DS_Store
       
   372                 if (list[0].toString().toLowerCase().endsWith(".ds_store") ||
       
   373                     list[1].toString().toLowerCase().endsWith(".ds_store")) {
       
   374                     return root; // Only app image and .DS_Store
       
   375                 }
       
   376             }
       
   377         }
       
   378 
       
   379         // Copy to new root
       
   380         Path newRoot = Files.createTempDirectory(
       
   381                 TEMP_ROOT.fetchFrom(params).toPath(),
       
   382                 "root-");
       
   383 
       
   384         IOUtils.copyRecursive(appLocation.toPath(),
       
   385                 newRoot.resolve(appLocation.getName()));
       
   386 
       
   387         return newRoot.toString();
       
   388     }
       
   389 
       
   390     private File createPKG(Map<String, ? super Object> params,
       
   391             File outdir, File appLocation) {
       
   392         // generic find attempt
       
   393         try {
       
   394             File appPKG = getPackages_AppPackage(params);
       
   395 
       
   396             String root = getRoot(params, appLocation);
       
   397 
       
   398             // Generate default CPL file
       
   399             File cpl = new File(CONFIG_ROOT.fetchFrom(params).getAbsolutePath()
       
   400                     + File.separator + "cpl.plist");
       
   401             ProcessBuilder pb = new ProcessBuilder("pkgbuild",
       
   402                     "--root",
       
   403                     root,
       
   404                     "--install-location",
       
   405                     MAC_INSTALL_DIR.fetchFrom(params),
       
   406                     "--analyze",
       
   407                     cpl.getAbsolutePath());
       
   408 
       
   409             IOUtils.exec(pb);
       
   410 
       
   411             patchCPLFile(cpl);
       
   412 
       
   413             preparePackageScripts(params);
       
   414 
       
   415             // build application package
       
   416             pb = new ProcessBuilder("pkgbuild",
       
   417                     "--root",
       
   418                     root,
       
   419                     "--install-location",
       
   420                     MAC_INSTALL_DIR.fetchFrom(params),
       
   421                     "--component-plist",
       
   422                     cpl.getAbsolutePath(),
       
   423                     "--scripts",
       
   424                     SCRIPTS_DIR.fetchFrom(params).getAbsolutePath(),
       
   425                     appPKG.getAbsolutePath());
       
   426             IOUtils.exec(pb);
       
   427 
       
   428             // build final package
       
   429             File finalPKG = new File(outdir, INSTALLER_NAME.fetchFrom(params)
       
   430                     + INSTALLER_SUFFIX.fetchFrom(params)
       
   431                     + ".pkg");
       
   432             outdir.mkdirs();
       
   433 
       
   434             List<String> commandLine = new ArrayList<>();
       
   435             commandLine.add("productbuild");
       
   436 
       
   437             commandLine.add("--resources");
       
   438             commandLine.add(CONFIG_ROOT.fetchFrom(params).getAbsolutePath());
       
   439 
       
   440             // maybe sign
       
   441             if (Optional.ofNullable(MacAppImageBuilder.
       
   442                     SIGN_BUNDLE.fetchFrom(params)).orElse(Boolean.TRUE)) {
       
   443                 if (Platform.getMajorVersion() > 10 ||
       
   444                     (Platform.getMajorVersion() == 10 &&
       
   445                     Platform.getMinorVersion() >= 12)) {
       
   446                     // we need this for OS X 10.12+
       
   447                     Log.verbose(I18N.getString("message.signing.pkg"));
       
   448                 }
       
   449 
       
   450                 String signingIdentity =
       
   451                         DEVELOPER_ID_INSTALLER_SIGNING_KEY.fetchFrom(params);
       
   452                 if (signingIdentity != null) {
       
   453                     commandLine.add("--sign");
       
   454                     commandLine.add(signingIdentity);
       
   455                 }
       
   456 
       
   457                 String keychainName = SIGNING_KEYCHAIN.fetchFrom(params);
       
   458                 if (keychainName != null && !keychainName.isEmpty()) {
       
   459                     commandLine.add("--keychain");
       
   460                     commandLine.add(keychainName);
       
   461                 }
       
   462             }
       
   463 
       
   464             commandLine.add("--distribution");
       
   465             commandLine.add(
       
   466                     getConfig_DistributionXMLFile(params).getAbsolutePath());
       
   467             commandLine.add("--package-path");
       
   468             commandLine.add(PACKAGES_ROOT.fetchFrom(params).getAbsolutePath());
       
   469 
       
   470             commandLine.add(finalPKG.getAbsolutePath());
       
   471 
       
   472             pb = new ProcessBuilder(commandLine);
       
   473             IOUtils.exec(pb);
       
   474 
       
   475             return finalPKG;
       
   476         } catch (Exception ignored) {
       
   477             Log.verbose(ignored);
       
   478             return null;
       
   479         }
       
   480     }
       
   481 
       
   482     //////////////////////////////////////////////////////////////////////////
       
   483     // Implement Bundler
       
   484     //////////////////////////////////////////////////////////////////////////
       
   485 
       
   486     @Override
       
   487     public String getName() {
       
   488         return I18N.getString("pkg.bundler.name");
       
   489     }
       
   490 
       
   491     @Override
       
   492     public String getID() {
       
   493         return "pkg";
       
   494     }
       
   495 
       
   496     @Override
       
   497     public boolean validate(Map<String, ? super Object> params)
       
   498             throws ConfigException {
       
   499         try {
       
   500             Objects.requireNonNull(params);
       
   501 
       
   502             // run basic validation to ensure requirements are met
       
   503             // we are not interested in return code, only possible exception
       
   504             validateAppImageAndBundeler(params);
       
   505 
       
   506             if (MAC_CF_BUNDLE_IDENTIFIER.fetchFrom(params) == null) {
       
   507                 throw new ConfigException(
       
   508                         I18N.getString("message.app-image-requires-identifier"),
       
   509                         I18N.getString(
       
   510                             "message.app-image-requires-identifier.advice"));
       
   511             }
       
   512 
       
   513             // reject explicitly set sign to true and no valid signature key
       
   514             if (Optional.ofNullable(MacAppImageBuilder.
       
   515                     SIGN_BUNDLE.fetchFrom(params)).orElse(Boolean.FALSE)) {
       
   516                 String signingIdentity =
       
   517                         DEVELOPER_ID_INSTALLER_SIGNING_KEY.fetchFrom(params);
       
   518                 if (signingIdentity == null) {
       
   519                     throw new ConfigException(
       
   520                             I18N.getString("error.explicit-sign-no-cert"),
       
   521                             I18N.getString(
       
   522                             "error.explicit-sign-no-cert.advice"));
       
   523                 }
       
   524             }
       
   525 
       
   526             // hdiutil is always available so there's no need
       
   527             // to test for availability.
       
   528 
       
   529             return true;
       
   530         } catch (RuntimeException re) {
       
   531             if (re.getCause() instanceof ConfigException) {
       
   532                 throw (ConfigException) re.getCause();
       
   533             } else {
       
   534                 throw new ConfigException(re);
       
   535             }
       
   536         }
       
   537     }
       
   538 
       
   539     @Override
       
   540     public File execute(Map<String, ? super Object> params,
       
   541             File outputParentDir) throws PackagerException {
       
   542         return bundle(params, outputParentDir);
       
   543     }
       
   544 
       
   545     @Override
       
   546     public boolean supported(boolean runtimeInstaller) {
       
   547         return true;
       
   548     }
       
   549 
       
   550     @Override
       
   551     public boolean isDefault() {
       
   552         return false;
       
   553     }
       
   554 
       
   555 }