src/jdk.packager/linux/classes/jdk/packager/internal/linux/LinuxRpmBundler.java
branchJDK-8200758-branch
changeset 57017 1b08af362a30
parent 57016 f63f13da91c0
child 57018 9d782e357916
equal deleted inserted replaced
57016:f63f13da91c0 57017:1b08af362a30
     1 /*
       
     2  * Copyright (c) 2012, 2018, 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.packager.internal.linux;
       
    27 
       
    28 import jdk.packager.internal.*;
       
    29 import jdk.packager.internal.Arguments;
       
    30 import jdk.packager.internal.resources.linux.LinuxResources;
       
    31 
       
    32 import javax.imageio.ImageIO;
       
    33 import java.awt.image.BufferedImage;
       
    34 import java.io.*;
       
    35 import java.nio.file.Files;
       
    36 import java.nio.file.attribute.PosixFilePermission;
       
    37 import java.nio.file.attribute.PosixFilePermissions;
       
    38 import java.text.MessageFormat;
       
    39 import java.util.*;
       
    40 import java.util.logging.Level;
       
    41 import java.util.logging.Logger;
       
    42 import java.util.regex.Matcher;
       
    43 import java.util.regex.Pattern;
       
    44 
       
    45 import static jdk.packager.internal.StandardBundlerParam.*;
       
    46 import static jdk.packager.internal.linux.LinuxAppBundler.LINUX_INSTALL_DIR;
       
    47 import static
       
    48         jdk.packager.internal.linux.LinuxAppBundler.LINUX_PACKAGE_DEPENDENCIES;
       
    49 
       
    50 public class LinuxRpmBundler extends AbstractBundler {
       
    51 
       
    52     private static final ResourceBundle I18N = ResourceBundle.getBundle(
       
    53             "jdk.packager.internal.resources.linux.LinuxRpmBundler");
       
    54 
       
    55     public static final BundlerParamInfo<LinuxAppBundler> APP_BUNDLER =
       
    56             new StandardBundlerParam<>(
       
    57             I18N.getString("param.app-bundler.name"),
       
    58             I18N.getString("param.app-bundler.description"),
       
    59             "linux.app.bundler",
       
    60             LinuxAppBundler.class,
       
    61             params -> new LinuxAppBundler(),
       
    62             null);
       
    63 
       
    64     public static final BundlerParamInfo<File> RPM_IMAGE_DIR =
       
    65             new StandardBundlerParam<>(
       
    66             I18N.getString("param.image-dir.name"),
       
    67             I18N.getString("param.image-dir.description"),
       
    68             "linux.rpm.imageDir",
       
    69             File.class,
       
    70             params -> {
       
    71                 File imagesRoot = IMAGES_ROOT.fetchFrom(params);
       
    72                 if (!imagesRoot.exists()) imagesRoot.mkdirs();
       
    73                 return new File(imagesRoot, "linux-rpm.image");
       
    74             },
       
    75             (s, p) -> new File(s));
       
    76 
       
    77     public static final BundlerParamInfo<File> CONFIG_ROOT =
       
    78             new StandardBundlerParam<>(
       
    79             I18N.getString("param.config-root.name"),
       
    80             I18N.getString("param.config-root.description"),
       
    81             "configRoot",
       
    82             File.class,
       
    83             params ->  new File(BUILD_ROOT.fetchFrom(params), "linux"),
       
    84             (s, p) -> new File(s));
       
    85 
       
    86     // Fedora rules for package naming are used here
       
    87     // https://fedoraproject.org/wiki/Packaging:NamingGuidelines?rd=Packaging/NamingGuidelines
       
    88     //
       
    89     // all Fedora packages must be named using only the following ASCII
       
    90     // characters. These characters are displayed here:
       
    91     //
       
    92     // abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._+
       
    93     //
       
    94     private static final Pattern RPM_BUNDLE_NAME_PATTERN =
       
    95             Pattern.compile("[a-z\\d\\+\\-\\.\\_]+", Pattern.CASE_INSENSITIVE);
       
    96 
       
    97     public static final BundlerParamInfo<String> BUNDLE_NAME =
       
    98             new StandardBundlerParam<> (
       
    99             I18N.getString("param.bundle-name.name"),
       
   100             I18N.getString("param.bundle-name.description"),
       
   101             Arguments.CLIOptions.LINUX_BUNDLE_NAME.getId(),
       
   102             String.class,
       
   103             params -> {
       
   104                 String nm = APP_NAME.fetchFrom(params);
       
   105                 if (nm == null) return null;
       
   106 
       
   107                 // make sure to lower case and spaces become dashes
       
   108                 nm = nm.toLowerCase().replaceAll("[ ]", "-");
       
   109 
       
   110                 return nm;
       
   111             },
       
   112             (s, p) -> {
       
   113                 if (!RPM_BUNDLE_NAME_PATTERN.matcher(s).matches()) {
       
   114                     String msgKey = "error.invalid-value-for-package-name";
       
   115                     throw new IllegalArgumentException(
       
   116                             new ConfigException(MessageFormat.format(
       
   117                                     I18N.getString(msgKey), s),
       
   118                                     I18N.getString(msgKey + ".advice")));
       
   119                 }
       
   120 
       
   121                 return s;
       
   122             }
       
   123         );
       
   124 
       
   125     public static final BundlerParamInfo<String> LICENSE_TYPE =
       
   126         new StandardBundlerParam<>(
       
   127                 I18N.getString("param.license-type.name"),
       
   128                 I18N.getString("param.license-type.description"),
       
   129                 Arguments.CLIOptions.LINUX_RPM_LICENSE_TYPE.getId(),
       
   130                 String.class,
       
   131                 params -> I18N.getString("param.license-type.default"),
       
   132                 (s, p) -> s
       
   133         );
       
   134 
       
   135     public static final BundlerParamInfo<String> XDG_FILE_PREFIX =
       
   136             new StandardBundlerParam<> (
       
   137             I18N.getString("param.xdg-prefix.name"),
       
   138             I18N.getString("param.xdg-prefix.description"),
       
   139             "linux.xdg-prefix",
       
   140             String.class,
       
   141             params -> {
       
   142                 try {
       
   143                     String vendor;
       
   144                     if (params.containsKey(VENDOR.getID())) {
       
   145                         vendor = VENDOR.fetchFrom(params);
       
   146                     } else {
       
   147                         vendor = "jpackager";
       
   148                     }
       
   149                     String appName = APP_FS_NAME.fetchFrom(params);
       
   150 
       
   151                     return (vendor + "-" + appName).replaceAll("\\s", "");
       
   152                 } catch (Exception e) {
       
   153                     if (Log.isDebug()) {
       
   154                         e.printStackTrace();
       
   155                     }
       
   156                 }
       
   157                 return "unknown-MimeInfo.xml";
       
   158             },
       
   159             (s, p) -> s);
       
   160 
       
   161     private final static String DEFAULT_ICON = "javalogo_white_32.png";
       
   162     private final static String DEFAULT_SPEC_TEMPLATE = "template.spec";
       
   163     private final static String DEFAULT_DESKTOP_FILE_TEMPLATE =
       
   164             "template.desktop";
       
   165 
       
   166     public final static String TOOL_RPMBUILD = "rpmbuild";
       
   167     public final static double TOOL_RPMBUILD_MIN_VERSION = 4.0d;
       
   168 
       
   169     public LinuxRpmBundler() {
       
   170         super();
       
   171         baseResourceLoader = LinuxResources.class;
       
   172     }
       
   173 
       
   174     public static boolean testTool(String toolName, double minVersion) {
       
   175         try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
       
   176                 PrintStream ps = new PrintStream(baos)) {
       
   177             ProcessBuilder pb = new ProcessBuilder(toolName, "--version");
       
   178             IOUtils.exec(pb, Log.isDebug(), false, ps);
       
   179                     //not interested in the above's output
       
   180             String content = new String(baos.toByteArray());
       
   181             Pattern pattern = Pattern.compile(" (\\d+\\.\\d+)");
       
   182             Matcher matcher = pattern.matcher(content);
       
   183 
       
   184             if (matcher.find()) {
       
   185                 String v = matcher.group(1);
       
   186                 double version = Double.parseDouble(v);
       
   187                 return minVersion <= version;
       
   188             } else {
       
   189                return false;
       
   190             }
       
   191         } catch (Exception e) {
       
   192             Log.verbose(MessageFormat.format(I18N.getString(
       
   193                     "message.test-for-tool"), toolName, e.getMessage()));
       
   194             return false;
       
   195         }
       
   196     }
       
   197 
       
   198     @Override
       
   199     public boolean validate(Map<String, ? super Object> p)
       
   200             throws UnsupportedPlatformException, ConfigException {
       
   201         try {
       
   202             if (p == null) throw new ConfigException(
       
   203                     I18N.getString("error.parameters-null"),
       
   204                     I18N.getString("error.parameters-null.advice"));
       
   205 
       
   206             // run basic validation to ensure requirements are met
       
   207             // we are not interested in return code, only possible exception
       
   208             APP_BUNDLER.fetchFrom(p).doValidate(p);
       
   209 
       
   210             // validate license file, if used, exists in the proper place
       
   211             if (p.containsKey(LICENSE_FILE.getID())) {
       
   212                 List<RelativeFileSet> appResourcesList =
       
   213                         APP_RESOURCES_LIST.fetchFrom(p);
       
   214                 for (String license : LICENSE_FILE.fetchFrom(p)) {
       
   215                     boolean found = false;
       
   216                     for (RelativeFileSet appResources : appResourcesList) {
       
   217                         found = found || appResources.contains(license);
       
   218                     }
       
   219                     if (!found) {
       
   220                         throw new ConfigException(
       
   221                                 I18N.getString("error.license-missing"),
       
   222                                 MessageFormat.format(
       
   223                                 I18N.getString("error.license-missing.advice"),
       
   224                                 license));
       
   225                     }
       
   226                 }
       
   227             }
       
   228 
       
   229             // validate presense of required tools
       
   230             if (!testTool(TOOL_RPMBUILD, TOOL_RPMBUILD_MIN_VERSION)){
       
   231                 throw new ConfigException(
       
   232                     MessageFormat.format(
       
   233                         I18N.getString("error.cannot-find-rpmbuild"),
       
   234                         TOOL_RPMBUILD_MIN_VERSION),
       
   235                     MessageFormat.format(
       
   236                         I18N.getString("error.cannot-find-rpmbuild.advice"),
       
   237                         TOOL_RPMBUILD_MIN_VERSION));
       
   238             }
       
   239 
       
   240             // only one mime type per association, at least one file extension
       
   241             List<Map<String, ? super Object>> associations =
       
   242                     FILE_ASSOCIATIONS.fetchFrom(p);
       
   243             if (associations != null) {
       
   244                 for (int i = 0; i < associations.size(); i++) {
       
   245                     Map<String, ? super Object> assoc = associations.get(i);
       
   246                     List<String> mimes = FA_CONTENT_TYPE.fetchFrom(assoc);
       
   247                     if (mimes == null || mimes.isEmpty()) {
       
   248                         String msgKey =
       
   249                                 "error.no-content-types-for-file-association";
       
   250                         throw new ConfigException(
       
   251                                 MessageFormat.format(I18N.getString(msgKey), i),
       
   252                                 I18N.getString(msgKey + ".advice"));
       
   253                     } else if (mimes.size() > 1) {
       
   254                         String msgKey =
       
   255                                 "error.no-content-types-for-file-association";
       
   256                         throw new ConfigException(
       
   257                                 MessageFormat.format(I18N.getString(msgKey), i),
       
   258                                 I18N.getString(msgKey + ".advice"));
       
   259                     }
       
   260                 }
       
   261             }
       
   262 
       
   263             // bundle name has some restrictions
       
   264             // the string converter will throw an exception if invalid
       
   265             BUNDLE_NAME.getStringConverter().apply(BUNDLE_NAME.fetchFrom(p), p);
       
   266 
       
   267             return true;
       
   268         } catch (RuntimeException re) {
       
   269             if (re.getCause() instanceof ConfigException) {
       
   270                 throw (ConfigException) re.getCause();
       
   271             } else {
       
   272                 throw new ConfigException(re);
       
   273             }
       
   274         }
       
   275     }
       
   276 
       
   277     private boolean prepareProto(Map<String, ? super Object> p)
       
   278             throws IOException {
       
   279         File appImage = StandardBundlerParam.getPredefinedAppImage(p);
       
   280         File appDir = null;
       
   281 
       
   282         // we either have an application image or need to build one
       
   283         if (appImage != null) {
       
   284             appDir = new File(RPM_IMAGE_DIR.fetchFrom(p),
       
   285                 APP_NAME.fetchFrom(p));
       
   286             // copy everything from appImage dir into appDir/name
       
   287             IOUtils.copyRecursive(appImage.toPath(), appDir.toPath());
       
   288         } else {
       
   289             appDir = APP_BUNDLER.fetchFrom(p).doBundle(p,
       
   290                     RPM_IMAGE_DIR.fetchFrom(p), true);
       
   291         }
       
   292         return appDir != null;
       
   293     }
       
   294 
       
   295     public File bundle(Map<String, ? super Object> p, File outdir) {
       
   296         if (!outdir.isDirectory() && !outdir.mkdirs()) {
       
   297             throw new RuntimeException(MessageFormat.format(
       
   298                     I18N.getString("error.cannot-create-output-dir"),
       
   299                     outdir.getAbsolutePath()));
       
   300         }
       
   301         if (!outdir.canWrite()) {
       
   302             throw new RuntimeException(MessageFormat.format(
       
   303                     I18N.getString("error.cannot-write-to-output-dir"),
       
   304                     outdir.getAbsolutePath()));
       
   305         }
       
   306 
       
   307         File imageDir = RPM_IMAGE_DIR.fetchFrom(p);
       
   308         try {
       
   309 
       
   310             imageDir.mkdirs();
       
   311 
       
   312             if (prepareProto(p) && prepareProjectConfig(p)) {
       
   313                 return buildRPM(p, outdir);
       
   314             }
       
   315             return null;
       
   316         } catch (IOException ex) {
       
   317             ex.printStackTrace();
       
   318             return null;
       
   319         } finally {
       
   320             try {
       
   321                 if (imageDir != null &&
       
   322                         PREDEFINED_APP_IMAGE.fetchFrom(p) == null &&
       
   323                         (PREDEFINED_RUNTIME_IMAGE.fetchFrom(p) == null ||
       
   324                         !Arguments.CREATE_JRE_INSTALLER.fetchFrom(p)) &&
       
   325                         !Log.isDebug()) {
       
   326                     IOUtils.deleteRecursive(imageDir);
       
   327                 } else if (imageDir != null) {
       
   328                     Log.info(MessageFormat.format(I18N.getString(
       
   329                             "message.debug-working-directory"),
       
   330                             imageDir.getAbsolutePath()));
       
   331                 }
       
   332             } catch (IOException ex) {
       
   333                 // noinspection ReturnInsideFinallyBlock
       
   334                 Log.debug(ex.getMessage());
       
   335                 return null;
       
   336             }
       
   337         }
       
   338     }
       
   339 
       
   340     /*
       
   341      * set permissions with a string like "rwxr-xr-x"
       
   342      *
       
   343      * This cannot be directly backport to 22u which is built with 1.6
       
   344      */
       
   345     private void setPermissions(File file, String permissions) {
       
   346         Set<PosixFilePermission> filePermissions =
       
   347                 PosixFilePermissions.fromString(permissions);
       
   348         try {
       
   349             if (file.exists()) {
       
   350                 Files.setPosixFilePermissions(file.toPath(), filePermissions);
       
   351             }
       
   352         } catch (IOException ex) {
       
   353             Logger.getLogger(LinuxDebBundler.class.getName()).log(
       
   354                     Level.SEVERE, null, ex);
       
   355         }
       
   356     }
       
   357 
       
   358     private String getLicenseFileString(Map<String, ? super Object> params) {
       
   359         StringBuilder sb = new StringBuilder();
       
   360         for (String f: LICENSE_FILE.fetchFrom(params)) {
       
   361             if (sb.length() != 0) {
       
   362                 sb.append("\n");
       
   363             }
       
   364             sb.append("%doc ");
       
   365             sb.append(LINUX_INSTALL_DIR.fetchFrom(params));
       
   366             sb.append("/");
       
   367             sb.append(APP_FS_NAME.fetchFrom(params));
       
   368             sb.append("/app/");
       
   369             sb.append(f);
       
   370         }
       
   371         return sb.toString();
       
   372     }
       
   373 
       
   374     private boolean prepareProjectConfig(Map<String, ? super Object> params)
       
   375             throws IOException {
       
   376         Map<String, String> data = createReplacementData(params);
       
   377         File rootDir =
       
   378             LinuxAppBundler.getRootDir(RPM_IMAGE_DIR.fetchFrom(params), params);
       
   379 
       
   380         // prepare installer icon
       
   381         File iconTarget = getConfig_IconFile(rootDir, params);
       
   382         File icon = LinuxAppBundler.ICON_PNG.fetchFrom(params);
       
   383         if (!Arguments.CREATE_JRE_INSTALLER.fetchFrom(params)) {
       
   384             if (icon == null || !icon.exists()) {
       
   385                 fetchResource(LinuxAppBundler.LINUX_BUNDLER_PREFIX
       
   386                         + iconTarget.getName(),
       
   387                         I18N.getString("resource.menu-icon"),
       
   388                         DEFAULT_ICON,
       
   389                         iconTarget,
       
   390                         VERBOSE.fetchFrom(params),
       
   391                         DROP_IN_RESOURCES_ROOT.fetchFrom(params));
       
   392             } else {
       
   393                 fetchResource(LinuxAppBundler.LINUX_BUNDLER_PREFIX
       
   394                         + iconTarget.getName(),
       
   395                         I18N.getString("resource.menu-icon"),
       
   396                         icon,
       
   397                         iconTarget,
       
   398                         VERBOSE.fetchFrom(params),
       
   399                         DROP_IN_RESOURCES_ROOT.fetchFrom(params));
       
   400             }
       
   401         }
       
   402 
       
   403         StringBuilder installScripts = new StringBuilder();
       
   404         StringBuilder removeScripts = new StringBuilder();
       
   405         for (Map<String, ? super Object> secondaryLauncher :
       
   406                 SECONDARY_LAUNCHERS.fetchFrom(params)) {
       
   407             Map<String, String> secondaryLauncherData =
       
   408                     createReplacementData(secondaryLauncher);
       
   409             secondaryLauncherData.put("APPLICATION_FS_NAME",
       
   410                     data.get("APPLICATION_FS_NAME"));
       
   411             secondaryLauncherData.put("DESKTOP_MIMES", "");
       
   412 
       
   413             // prepare desktop shortcut
       
   414             Writer w = new BufferedWriter(new FileWriter(
       
   415                     getConfig_DesktopShortcutFile(rootDir, secondaryLauncher)));
       
   416             String content = preprocessTextResource(
       
   417                     LinuxAppBundler.LINUX_BUNDLER_PREFIX
       
   418                             + getConfig_DesktopShortcutFile(rootDir,
       
   419                                     secondaryLauncher).getName(),
       
   420                     I18N.getString("resource.menu-shortcut-descriptor"),
       
   421                     DEFAULT_DESKTOP_FILE_TEMPLATE, secondaryLauncherData,
       
   422                     VERBOSE.fetchFrom(params),
       
   423                     DROP_IN_RESOURCES_ROOT.fetchFrom(params));
       
   424             w.write(content);
       
   425             w.close();
       
   426 
       
   427             // prepare installer icon
       
   428             iconTarget = getConfig_IconFile(rootDir, secondaryLauncher);
       
   429             icon = LinuxAppBundler.ICON_PNG.fetchFrom(secondaryLauncher);
       
   430             if (icon == null || !icon.exists()) {
       
   431                 fetchResource(LinuxAppBundler.LINUX_BUNDLER_PREFIX
       
   432                         + iconTarget.getName(),
       
   433                         I18N.getString("resource.menu-icon"),
       
   434                         DEFAULT_ICON,
       
   435                         iconTarget,
       
   436                         VERBOSE.fetchFrom(params),
       
   437                         DROP_IN_RESOURCES_ROOT.fetchFrom(params));
       
   438             } else {
       
   439                 fetchResource(LinuxAppBundler.LINUX_BUNDLER_PREFIX
       
   440                         + iconTarget.getName(),
       
   441                         I18N.getString("resource.menu-icon"),
       
   442                         icon,
       
   443                         iconTarget,
       
   444                         VERBOSE.fetchFrom(params),
       
   445                         DROP_IN_RESOURCES_ROOT.fetchFrom(params));
       
   446             }
       
   447 
       
   448             // post copying of desktop icon
       
   449             installScripts.append("xdg-desktop-menu install --novendor ");
       
   450             installScripts.append(LINUX_INSTALL_DIR.fetchFrom(params));
       
   451             installScripts.append("/");
       
   452             installScripts.append(data.get("APPLICATION_FS_NAME"));
       
   453             installScripts.append("/");
       
   454             installScripts.append(secondaryLauncherData.get(
       
   455                     "APPLICATION_LAUNCHER_FILENAME"));
       
   456             installScripts.append(".desktop\n");
       
   457 
       
   458             // preun cleanup of desktop icon
       
   459             removeScripts.append("xdg-desktop-menu uninstall --novendor ");
       
   460             removeScripts.append(LINUX_INSTALL_DIR.fetchFrom(params));
       
   461             removeScripts.append("/");
       
   462             removeScripts.append(data.get("APPLICATION_FS_NAME"));
       
   463             removeScripts.append("/");
       
   464             removeScripts.append(secondaryLauncherData.get(
       
   465                     "APPLICATION_LAUNCHER_FILENAME"));
       
   466             removeScripts.append(".desktop\n");
       
   467 
       
   468         }
       
   469         data.put("SECONDARY_LAUNCHERS_INSTALL", installScripts.toString());
       
   470         data.put("SECONDARY_LAUNCHERS_REMOVE", removeScripts.toString());
       
   471 
       
   472         StringBuilder cdsScript = new StringBuilder();
       
   473 
       
   474         data.put("APP_CDS_CACHE", cdsScript.toString());
       
   475 
       
   476         List<Map<String, ? super Object>> associations =
       
   477                 FILE_ASSOCIATIONS.fetchFrom(params);
       
   478         data.put("FILE_ASSOCIATION_INSTALL", "");
       
   479         data.put("FILE_ASSOCIATION_REMOVE", "");
       
   480         data.put("DESKTOP_MIMES", "");
       
   481         if (associations != null) {
       
   482             String mimeInfoFile = XDG_FILE_PREFIX.fetchFrom(params)
       
   483                     + "-MimeInfo.xml";
       
   484             StringBuilder mimeInfo = new StringBuilder(
       
   485                 "<?xml version=\"1.0\"?>\n<mime-info xmlns="
       
   486                 +"'http://www.freedesktop.org/standards/shared-mime-info'>\n");
       
   487             StringBuilder registrations = new StringBuilder();
       
   488             StringBuilder deregistrations = new StringBuilder();
       
   489             StringBuilder desktopMimes = new StringBuilder("MimeType=");
       
   490             boolean addedEntry = false;
       
   491 
       
   492             for (Map<String, ? super Object> assoc : associations) {
       
   493                 //  <mime-type type="application/x-vnd.awesome">
       
   494                 //    <comment>Awesome document</comment>
       
   495                 //    <glob pattern="*.awesome"/>
       
   496                 //    <glob pattern="*.awe"/>
       
   497                 //  </mime-type>
       
   498 
       
   499                 if (assoc == null) {
       
   500                     continue;
       
   501                 }
       
   502 
       
   503                 String description = FA_DESCRIPTION.fetchFrom(assoc);
       
   504                 File faIcon = FA_ICON.fetchFrom(assoc); //TODO FA_ICON_PNG
       
   505                 List<String> extensions = FA_EXTENSIONS.fetchFrom(assoc);
       
   506                 if (extensions == null) {
       
   507                     Log.info(I18N.getString(
       
   508                         "message.creating-association-with-null-extension"));
       
   509                 }
       
   510 
       
   511                 List<String> mimes = FA_CONTENT_TYPE.fetchFrom(assoc);
       
   512                 if (mimes == null || mimes.isEmpty()) {
       
   513                     continue;
       
   514                 }
       
   515                 String thisMime = mimes.get(0);
       
   516                 String dashMime = thisMime.replace('/', '-');
       
   517 
       
   518                 mimeInfo.append("  <mime-type type='")
       
   519                         .append(thisMime)
       
   520                         .append("'>\n");
       
   521                 if (description != null && !description.isEmpty()) {
       
   522                     mimeInfo.append("    <comment>")
       
   523                             .append(description)
       
   524                             .append("</comment>\n");
       
   525                 }
       
   526 
       
   527                 if (extensions != null) {
       
   528                     for (String ext : extensions) {
       
   529                         mimeInfo.append("    <glob pattern='*.")
       
   530                                 .append(ext)
       
   531                                 .append("'/>\n");
       
   532                     }
       
   533                 }
       
   534 
       
   535                 mimeInfo.append("  </mime-type>\n");
       
   536                 if (!addedEntry) {
       
   537                     registrations.append("xdg-mime install ")
       
   538                             .append(LINUX_INSTALL_DIR.fetchFrom(params))
       
   539                             .append("/")
       
   540                             .append(data.get("APPLICATION_FS_NAME"))
       
   541                             .append("/")
       
   542                             .append(mimeInfoFile)
       
   543                             .append("\n");
       
   544 
       
   545                     deregistrations.append("xdg-mime uninstall ")
       
   546                             .append(LINUX_INSTALL_DIR.fetchFrom(params))
       
   547                             .append("/")
       
   548                             .append(data.get("APPLICATION_FS_NAME"))
       
   549                             .append("/")
       
   550                             .append(mimeInfoFile)
       
   551                             .append("\n");
       
   552                     addedEntry = true;
       
   553                 } else {
       
   554                     desktopMimes.append(";");
       
   555                 }
       
   556                 desktopMimes.append(thisMime);
       
   557 
       
   558                 if (faIcon != null && faIcon.exists()) {
       
   559                     int size = getSquareSizeOfImage(faIcon);
       
   560 
       
   561                     if (size > 0) {
       
   562                         File target = new File(rootDir,
       
   563                                 APP_FS_NAME.fetchFrom(params)
       
   564                                         + "_fa_" + faIcon.getName());
       
   565                         IOUtils.copyFile(faIcon, target);
       
   566 
       
   567                         // xdg-icon-resource install --context mimetypes
       
   568                         // --size 64 awesomeapp_fa_1.png
       
   569                         // application-x.vnd-awesome
       
   570                         registrations.append(
       
   571                                 "xdg-icon-resource install "
       
   572                                 + "--context mimetypes --size ")
       
   573                                 .append(size)
       
   574                                 .append(" ")
       
   575                                 .append(LINUX_INSTALL_DIR.fetchFrom(params))
       
   576                                 .append("/")
       
   577                                 .append(data.get("APPLICATION_FS_NAME"))
       
   578                                 .append("/")
       
   579                                 .append(target.getName())
       
   580                                 .append(" ")
       
   581                                 .append(dashMime)
       
   582                                 .append("\n");
       
   583 
       
   584                         // xdg-icon-resource uninstall --context mimetypes
       
   585                         // --size 64 awesomeapp_fa_1.png
       
   586                         // application-x.vnd-awesome
       
   587                         deregistrations.append(
       
   588                                 "xdg-icon-resource uninstall "
       
   589                                 + "--context mimetypes --size ")
       
   590                                 .append(size)
       
   591                                 .append(" ")
       
   592                                 .append(LINUX_INSTALL_DIR.fetchFrom(params))
       
   593                                 .append("/")
       
   594                                 .append(data.get("APPLICATION_FS_NAME"))
       
   595                                 .append("/")
       
   596                                 .append(target.getName())
       
   597                                 .append(" ")
       
   598                                 .append(dashMime)
       
   599                                 .append("\n");
       
   600                     }
       
   601                 }
       
   602             }
       
   603             mimeInfo.append("</mime-info>");
       
   604 
       
   605             if (addedEntry) {
       
   606                 Writer w = new BufferedWriter(new FileWriter(
       
   607                         new File(rootDir, mimeInfoFile)));
       
   608                 w.write(mimeInfo.toString());
       
   609                 w.close();
       
   610                 data.put("FILE_ASSOCIATION_INSTALL", registrations.toString());
       
   611                 data.put("FILE_ASSOCIATION_REMOVE", deregistrations.toString());
       
   612                 data.put("DESKTOP_MIMES", desktopMimes.toString());
       
   613             }
       
   614         }
       
   615 
       
   616         if (!Arguments.CREATE_JRE_INSTALLER.fetchFrom(params)) {
       
   617             //prepare desktop shortcut
       
   618             Writer w = new BufferedWriter(new FileWriter(
       
   619                     getConfig_DesktopShortcutFile(rootDir, params)));
       
   620             String content = preprocessTextResource(
       
   621                     LinuxAppBundler.LINUX_BUNDLER_PREFIX
       
   622                             + getConfig_DesktopShortcutFile(rootDir,
       
   623                                     params).getName(),
       
   624                     I18N.getString("resource.menu-shortcut-descriptor"),
       
   625                     DEFAULT_DESKTOP_FILE_TEMPLATE, data,
       
   626                     VERBOSE.fetchFrom(params),
       
   627                     DROP_IN_RESOURCES_ROOT.fetchFrom(params));
       
   628             w.write(content);
       
   629             w.close();
       
   630         }
       
   631 
       
   632         // prepare spec file
       
   633         Writer w = new BufferedWriter(
       
   634                 new FileWriter(getConfig_SpecFile(params)));
       
   635         String content = preprocessTextResource(
       
   636                 LinuxAppBundler.LINUX_BUNDLER_PREFIX
       
   637                         + getConfig_SpecFile(params).getName(),
       
   638                 I18N.getString("resource.rpm-spec-file"),
       
   639                 DEFAULT_SPEC_TEMPLATE, data,
       
   640                 VERBOSE.fetchFrom(params),
       
   641                 DROP_IN_RESOURCES_ROOT.fetchFrom(params));
       
   642         w.write(content);
       
   643         w.close();
       
   644 
       
   645         return true;
       
   646     }
       
   647 
       
   648     private Map<String, String> createReplacementData(
       
   649             Map<String, ? super Object> params) {
       
   650         Map<String, String> data = new HashMap<>();
       
   651 
       
   652         data.put("APPLICATION_NAME", APP_NAME.fetchFrom(params));
       
   653         data.put("APPLICATION_FS_NAME", APP_FS_NAME.fetchFrom(params));
       
   654         data.put("APPLICATION_PACKAGE", BUNDLE_NAME.fetchFrom(params));
       
   655         data.put("APPLICATION_VENDOR", VENDOR.fetchFrom(params));
       
   656         data.put("APPLICATION_VERSION", VERSION.fetchFrom(params));
       
   657         data.put("APPLICATION_LAUNCHER_FILENAME",
       
   658                 APP_FS_NAME.fetchFrom(params));
       
   659         data.put("INSTALLATION_DIRECTORY", LINUX_INSTALL_DIR.fetchFrom(params));
       
   660         data.put("XDG_PREFIX", XDG_FILE_PREFIX.fetchFrom(params));
       
   661         data.put("DEPLOY_BUNDLE_CATEGORY", CATEGORY.fetchFrom(params));
       
   662         // TODO rpm categories
       
   663         data.put("APPLICATION_DESCRIPTION", DESCRIPTION.fetchFrom(params));
       
   664         data.put("APPLICATION_SUMMARY", TITLE.fetchFrom(params));
       
   665         data.put("APPLICATION_LICENSE_TYPE", LICENSE_TYPE.fetchFrom(params));
       
   666         data.put("APPLICATION_LICENSE_FILE", getLicenseFileString(params));
       
   667         String deps = LINUX_PACKAGE_DEPENDENCIES.fetchFrom(params);
       
   668         data.put("PACKAGE_DEPENDENCIES",
       
   669                 deps.isEmpty() ? "" : "Requires: " + deps);
       
   670         data.put("CREATE_JRE_INSTALLER",
       
   671                 Arguments.CREATE_JRE_INSTALLER.fetchFrom(params).toString());
       
   672         return data;
       
   673     }
       
   674 
       
   675     private File getConfig_DesktopShortcutFile(File rootDir,
       
   676             Map<String, ? super Object> params) {
       
   677         return new File(rootDir,
       
   678                 APP_FS_NAME.fetchFrom(params) + ".desktop");
       
   679     }
       
   680 
       
   681     private File getConfig_IconFile(File rootDir,
       
   682             Map<String, ? super Object> params) {
       
   683         return new File(rootDir,
       
   684                 APP_FS_NAME.fetchFrom(params) + ".png");
       
   685     }
       
   686 
       
   687     private File getConfig_SpecFile(Map<String, ? super Object> params) {
       
   688         return new File(RPM_IMAGE_DIR.fetchFrom(params),
       
   689                 APP_FS_NAME.fetchFrom(params) + ".spec");
       
   690     }
       
   691 
       
   692     private File buildRPM(Map<String, ? super Object> params,
       
   693             File outdir) throws IOException {
       
   694         Log.verbose(MessageFormat.format(I18N.getString(
       
   695                 "message.outputting-bundle-location"),
       
   696                 outdir.getAbsolutePath()));
       
   697 
       
   698         File broot = new File(BUILD_ROOT.fetchFrom(params), "rmpbuildroot");
       
   699 
       
   700         outdir.mkdirs();
       
   701 
       
   702         //run rpmbuild
       
   703         ProcessBuilder pb = new ProcessBuilder(
       
   704                 TOOL_RPMBUILD,
       
   705                 "-bb", getConfig_SpecFile(params).getAbsolutePath(),
       
   706                 "--define", "%_sourcedir "
       
   707                         + RPM_IMAGE_DIR.fetchFrom(params).getAbsolutePath(),
       
   708                 // save result to output dir
       
   709                 "--define", "%_rpmdir " + outdir.getAbsolutePath(),
       
   710                 // do not use other system directories to build as current user
       
   711                 "--define", "%_topdir " + broot.getAbsolutePath()
       
   712         );
       
   713         pb = pb.directory(RPM_IMAGE_DIR.fetchFrom(params));
       
   714         IOUtils.exec(pb, false);
       
   715 
       
   716         if (!Log.isDebug()) {
       
   717             IOUtils.deleteRecursive(broot);
       
   718         }
       
   719 
       
   720         Log.info(MessageFormat.format(
       
   721                 I18N.getString("message.output-bundle-location"),
       
   722                 outdir.getAbsolutePath()));
       
   723 
       
   724         // presume the result is the ".rpm" file with the newest modified time
       
   725         // not the best solution, but it is the most reliable
       
   726         File result = null;
       
   727         long lastModified = 0;
       
   728         File[] list = outdir.listFiles();
       
   729         if (list != null) {
       
   730             for (File f : list) {
       
   731                 if (f.getName().endsWith(".rpm") &&
       
   732                         f.lastModified() > lastModified) {
       
   733                     result = f;
       
   734                     lastModified = f.lastModified();
       
   735                 }
       
   736             }
       
   737         }
       
   738 
       
   739         return result;
       
   740     }
       
   741 
       
   742     @Override
       
   743     public String getName() {
       
   744         return I18N.getString("bundler.name");
       
   745     }
       
   746 
       
   747     @Override
       
   748     public String getDescription() {
       
   749         return I18N.getString("bundler.description");
       
   750     }
       
   751 
       
   752     @Override
       
   753     public String getID() {
       
   754         return "rpm";
       
   755     }
       
   756 
       
   757     @Override
       
   758     public String getBundleType() {
       
   759         return "INSTALLER";
       
   760     }
       
   761 
       
   762     @Override
       
   763     public Collection<BundlerParamInfo<?>> getBundleParameters() {
       
   764         Collection<BundlerParamInfo<?>> results = new LinkedHashSet<>();
       
   765         results.addAll(LinuxAppBundler.getAppBundleParameters());
       
   766         results.addAll(getRpmBundleParameters());
       
   767         return results;
       
   768     }
       
   769 
       
   770     public static Collection<BundlerParamInfo<?>> getRpmBundleParameters() {
       
   771         return Arrays.asList(
       
   772                 BUNDLE_NAME,
       
   773                 CATEGORY,
       
   774                 DESCRIPTION,
       
   775                 LinuxAppBundler.ICON_PNG,
       
   776                 LICENSE_FILE,
       
   777                 LICENSE_TYPE,
       
   778                 TITLE,
       
   779                 VENDOR
       
   780         );
       
   781     }
       
   782 
       
   783     @Override
       
   784     public File execute(
       
   785             Map<String, ? super Object> params, File outputParentDir) {
       
   786         return bundle(params, outputParentDir);
       
   787     }
       
   788 
       
   789     @Override
       
   790     public boolean supported() {
       
   791         return (Platform.getPlatform() == Platform.LINUX);
       
   792     }
       
   793 
       
   794     public int getSquareSizeOfImage(File f) {
       
   795         try {
       
   796             BufferedImage bi = ImageIO.read(f);
       
   797             if (bi.getWidth() == bi.getHeight()) {
       
   798                 return bi.getWidth();
       
   799             } else {
       
   800                 return 0;
       
   801             }
       
   802         } catch (Exception e) {
       
   803             e.printStackTrace();
       
   804             return 0;
       
   805         }
       
   806     }
       
   807 }