modules/fxpackager/src/main/java/com/sun/javafx/tools/packager/bundlers/LinuxRPMBundler.java

Print this page




  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 com.sun.javafx.tools.packager.bundlers;
  27 
  28 import com.oracle.bundlers.AbstractBundler;
  29 import com.oracle.bundlers.BundlerParamInfo;
  30 import com.oracle.bundlers.StandardBundlerParam;
  31 import com.sun.javafx.tools.packager.Log;
  32 import com.sun.javafx.tools.resource.linux.LinuxResources;
  33 
  34 import java.io.*;



  35 import java.text.MessageFormat;
  36 import java.util.*;


  37 import java.util.regex.Matcher;
  38 import java.util.regex.Pattern;
  39 
  40 import static com.oracle.bundlers.StandardBundlerParam.*;
  41 
  42 public class LinuxRPMBundler extends AbstractBundler {
  43 
  44     private static final ResourceBundle I18N =
  45             ResourceBundle.getBundle("com.oracle.bundlers.linux.LinuxRpmBundler");
  46 
  47     public static final BundlerParamInfo<LinuxAppBundler> APP_BUNDLER = new StandardBundlerParam<>(
  48             I18N.getString("param.app-bundler.name"), 
  49             I18N.getString("param.app-bundler.description"),
  50             "linuxAppBundler", //KEY
  51             LinuxAppBundler.class, null, params -> new LinuxAppBundler(), false, null);
  52 
  53     public static final BundlerParamInfo<File> IMAGE_DIR = new StandardBundlerParam<>(
  54             I18N.getString("param.image-dir.name"), 
  55             I18N.getString("param.image-dir.description"),
  56             "imageDir", //KEY
  57             File.class, null, params -> {
  58                 File imagesRoot = IMAGES_ROOT.fetchFrom(params);

  59                 return new File(imagesRoot, "linux-rpm.image");
  60             }, false, File::new);
  61 
  62     public static final BundlerParamInfo<File> CONFIG_ROOT = new StandardBundlerParam<>(
  63             I18N.getString("param.config-root.name"), 
  64             I18N.getString("param.config-root.description"),
  65             "configRoot", //KEY
  66             File.class, null, params ->  new File(BUILD_ROOT.fetchFrom(params), "linux"),
  67             false, File::new);
  68 
  69     public static final BundlerParamInfo<String> BUNDLE_NAME = new StandardBundlerParam<> (
  70             I18N.getString("param.bundle-name.name"), 
  71             I18N.getString("param.bundle-name.description"),
  72             "bundleName", //KEY
  73             String.class, null, params -> {
  74                 String nm = APP_NAME.fetchFrom(params);
  75                 if (nm == null) return null;
  76         
  77                 //spaces are not allowed in RPM package names
  78                 nm = nm.replaceAll(" ", "");
  79                 return nm;
  80             }, false, s -> s);
  81 
  82     private final static String DEFAULT_ICON = "javalogo_white_32.png";
  83     private final static String DEFAULT_SPEC_TEMPLATE = "template.spec";
  84     private final static String DEFAULT_DESKTOP_FILE_TEMPLATE = "template.desktop";

  85 
  86     public final static String TOOL_RPMBUILD = "rpmbuild";
  87     public final static double TOOL_RPMBUILD_MIN_VERSION = 4.0d;
  88 
  89     public LinuxRPMBundler() {
  90         super();
  91         baseResourceLoader = LinuxResources.class;
  92     }
  93 
  94     public static boolean testTool(String toolName, double minVersion) {
  95         try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); PrintStream ps = new PrintStream(baos)) {
  96             ProcessBuilder pb = new ProcessBuilder(
  97                     toolName,
  98                     "--version");
  99 
 100             IOUtils.exec(pb, Log.isDebug(), false, ps); //not interested in the output
 101 
 102             //TODO: Version is ignored; need to extract version string and compare!
 103             String content = new String(baos.toByteArray());
 104             Pattern pattern = Pattern.compile("RPM version (\\d+\\.\\d+)");
 105             Matcher matcher = pattern.matcher(content);
 106             if (matcher.find()) {
 107                 String v = matcher.group(1);
 108                 double version = new Double(v);
 109                 return minVersion <= version;
 110             } else {
 111                return false;
 112             }
 113         } catch (Exception e) {
 114             Log.verbose(MessageFormat.format(I18N.getString("message.test-for-tool"), toolName, e.getMessage()));
 115             return false;
 116         }
 117     }
 118 
 119     @Override
 120     public boolean validate(Map<String, ? super Object> p) throws UnsupportedPlatformException, ConfigException {

 121         if (p == null) throw new ConfigException(
 122                 I18N.getString("error.parameters-null"), 
 123                 I18N.getString("error.parameters-null.advice"));
 124 
 125         //run basic validation to ensure requirements are met
 126         //we are not interested in return code, only possible exception
 127         APP_BUNDLER.fetchFrom(p).doValidate(p);
 128 
 129         //TODO: validate presense of required tools?
 130         if (!testTool(TOOL_RPMBUILD, TOOL_RPMBUILD_MIN_VERSION)){
 131             throw new ConfigException(
 132                     I18N.getString(MessageFormat.format("error.cannot-find-rpmbuild", TOOL_RPMBUILD_MIN_VERSION)),
 133                     I18N.getString(MessageFormat.format("error.cannot-find-rpmbuild.advice", TOOL_RPMBUILD_MIN_VERSION)));
 134         }
 135 
 136         return true;



 137     }
 138 
 139     private boolean prepareProto(Map<String, ? super Object> params) {
 140         File imageDir = IMAGE_DIR.fetchFrom(params);
 141         File appDir = APP_BUNDLER.fetchFrom(params).doBundle(params, imageDir, true);
 142         return appDir != null;
 143     }
 144 
 145     public File bundle(Map<String, ? super Object> p, File outdir) {
 146         File imageDir = IMAGE_DIR.fetchFrom(p);
 147         try {
 148 
 149             imageDir.mkdirs();
 150 
 151             boolean menuShortcut = MENU_HINT.fetchFrom(p);
 152             boolean desktopShortcut = SHORTCUT_HINT.fetchFrom(p);
 153             if (!menuShortcut && !desktopShortcut) {
 154                 //both can not be false - user will not find the app
 155                 Log.verbose(I18N.getString("message.one-shortcut-required"));
 156                 p.put(MENU_HINT.getID(), true);
 157             }
 158 
 159             if (prepareProto(p) && prepareProjectConfig(p)) {
 160                 return buildRPM(p, outdir);
 161             }
 162             return null;
 163         } catch (IOException ex) {
 164             ex.printStackTrace();
 165             return null;
 166         } finally {
 167             try {
 168                 if (verbose) {
 169                     saveConfigFiles(p);
 170                 }
 171                 if (imageDir != null && !Log.isDebug()) {
 172                     IOUtils.deleteRecursive(imageDir);
 173                 } else if (imageDir != null) {
 174                     Log.info(MessageFormat.format(I18N.getString("message.debug-working-directory"), imageDir.getAbsolutePath()));
 175                 }
 176             } catch (FileNotFoundException ex) {
 177                 //noinspection ReturnInsideFinallyBlock
 178                 return null;
 179             }
 180         }
 181     }
 182 
















 183     protected void saveConfigFiles(Map<String, ? super Object> params) {
 184         try {
 185             File configRoot = CONFIG_ROOT.fetchFrom(params);
 186             if (getConfig_SpecFile(params).exists()) {
 187                 IOUtils.copyFile(getConfig_SpecFile(params),
 188                         new File(configRoot, getConfig_SpecFile(params).getName()));
 189             }
 190             if (getConfig_DesktopShortcutFile(params).exists()) {
 191                 IOUtils.copyFile(getConfig_DesktopShortcutFile(params),
 192                         new File(configRoot, getConfig_DesktopShortcutFile(params).getName()));
 193             }
 194             if (getConfig_IconFile(params).exists()) {
 195                 IOUtils.copyFile(getConfig_IconFile(params),
 196                         new File(configRoot, getConfig_IconFile(params).getName()));
 197             }






 198             Log.info(MessageFormat.format(I18N.getString("message.config-save-location"), configRoot.getAbsolutePath()));
 199         } catch (IOException ioe) {
 200             ioe.printStackTrace();
 201         }
 202     }
 203 
 204     @Override
 205     public String toString() {
 206         return getName();
 207     }
 208 
 209     private String getLicenseFileString(Map<String, ? super Object> params) {
 210         StringBuilder sb = new StringBuilder();
 211         for (String f: LICENSE_FILES.fetchFrom(params)) {
 212             if (sb.length() != 0) {
 213                 sb.append("\n");
 214             }
 215             sb.append("%doc /opt/");
 216             sb.append(BUNDLE_NAME.fetchFrom(params));
 217             sb.append("/app/");
 218             sb.append(f);
 219         }
 220         return sb.toString();
 221     }
 222 
 223     private boolean prepareProjectConfig(Map<String, ? super Object> params) throws IOException {
 224         Map<String, String> data = new HashMap<>();
 225 
 226         data.put("APPLICATION_NAME", BUNDLE_NAME.fetchFrom(params));

 227         data.put("APPLICATION_VENDOR", VENDOR.fetchFrom(params));
 228         data.put("APPLICATION_VERSION", VERSION.fetchFrom(params));
 229         data.put("APPLICATION_LAUNCHER_FILENAME",
 230                 LinuxAppBundler.getLauncher(IMAGE_DIR.fetchFrom(params), params).getName());
 231         data.put("APPLICATION_DESKTOP_SHORTCUT", SHORTCUT_HINT.fetchFrom(params) ? "returnTrue" : "returnFalse");
 232         data.put("APPLICATION_MENU_SHORTCUT", MENU_HINT.fetchFrom(params) ? "returnTrue" : "returnFalse");
 233         data.put("DEPLOY_BUNDLE_CATEGORY", CATEGORY.fetchFrom(params)); //TODO rpm categories
 234         data.put("APPLICATION_DESCRIPTION", DESCRIPTION.fetchFrom(params));
 235         data.put("APPLICATION_SUMMARY", TITLE.fetchFrom(params));
 236         data.put("APPLICATION_LICENSE_TYPE", LICENSE_TYPE.fetchFrom(params));
 237         data.put("APPLICATION_LICENSE_FILE", getLicenseFileString(params));




 238 
 239         //prepare spec file
 240         Writer w = new BufferedWriter(new FileWriter(getConfig_SpecFile(params)));
 241         String content = preprocessTextResource(
 242                 LinuxAppBundler.LINUX_BUNDLER_PREFIX + getConfig_SpecFile(params).getName(),
 243                 I18N.getString("resource.rpm-spec-file"), DEFAULT_SPEC_TEMPLATE, data);

 244         w.write(content);
 245         w.close();
 246 
 247         //prepare desktop shortcut
 248         w = new BufferedWriter(new FileWriter(getConfig_DesktopShortcutFile(params)));
 249         content = preprocessTextResource(
 250                 LinuxAppBundler.LINUX_BUNDLER_PREFIX + getConfig_DesktopShortcutFile(params).getName(),
 251                 I18N.getString("resource.menu-shortcut-descriptor"), DEFAULT_DESKTOP_FILE_TEMPLATE, data);

 252         w.write(content);
 253         w.close();
 254 
 255         //prepare installer icon
 256         File iconTarget = getConfig_IconFile(params);
 257         File icon = ICON.fetchFrom(params);
 258         if (icon == null || !icon.exists()) {
 259             fetchResource(LinuxAppBundler.LINUX_BUNDLER_PREFIX + iconTarget.getName(),
 260                     I18N.getString("resource.menu-icon"),
 261                     DEFAULT_ICON,
 262                     iconTarget);

 263         } else {
 264             fetchResource(LinuxAppBundler.LINUX_BUNDLER_PREFIX + iconTarget.getName(),
 265                     I18N.getString("resource.menu-icon"),
 266                     icon,
 267                     iconTarget);















 268         }
 269 
 270         return true;
 271     }
 272 
 273     private File getConfig_DesktopShortcutFile(Map<String, ? super Object> params) {
 274         return new File(LinuxAppBundler.getLauncher(IMAGE_DIR.fetchFrom(params), params).getParentFile(),
 275                 BUNDLE_NAME.fetchFrom(params) + ".desktop");
 276     }
 277 
 278     private File getConfig_IconFile(Map<String, ? super Object> params) {
 279         return new File(LinuxAppBundler.getLauncher(IMAGE_DIR.fetchFrom(params), params).getParentFile(),
 280                 BUNDLE_NAME.fetchFrom(params) + ".png");
 281     }
 282 





 283     private File getConfig_SpecFile(Map<String, ? super Object> params) {
 284         return new File(IMAGE_DIR.fetchFrom(params),
 285                 BUNDLE_NAME.fetchFrom(params) + ".spec");
 286     }
 287 
 288     private File buildRPM(Map<String, ? super Object> params, File outdir) throws IOException {
 289         Log.verbose(MessageFormat.format(I18N.getString("message.outputting-bundle-location"), outdir.getAbsolutePath()));
 290 
 291         File broot = new File(BUILD_ROOT.fetchFrom(params), "rmpbuildroot");
 292 
 293         outdir.mkdirs();
 294 
 295         //run rpmbuild
 296         ProcessBuilder pb = new ProcessBuilder(
 297                 TOOL_RPMBUILD,
 298                 "-bb", getConfig_SpecFile(params).getAbsolutePath(),
 299 //                "--define", "%__jar_repack %{nil}",  //debug: improves build time (but will require unpack to install?)
 300                 "--define", "%_sourcedir "+IMAGE_DIR.fetchFrom(params).getAbsolutePath(),
 301                 "--define", "%_rpmdir " + outdir.getAbsolutePath(), //save result to output dir
 302                 "--define", "%_topdir " + broot.getAbsolutePath() //do not use other system directories to build as current user
 303         );
 304         pb = pb.directory(IMAGE_DIR.fetchFrom(params));
 305         IOUtils.exec(pb, verbose);
 306 
 307         IOUtils.deleteRecursive(broot);
 308 
 309         Log.info(MessageFormat.format(I18N.getString("message.output-bundle-location"), outdir.getAbsolutePath()));
 310 
 311         //todo look for added files and returnt hat added file.
 312         return outdir;













 313     }
 314 
 315     @Override
 316     public String getName() {
 317         return I18N.getString("bundler.name");
 318     }
 319 
 320     @Override
 321     public String getDescription() {
 322         return I18N.getString("bundler.description");
 323     }
 324 
 325     @Override
 326     public String getID() {
 327         return "rpm"; //KEY
 328     }
 329 
 330     @Override
 331     public BundleType getBundleType() {
 332         return BundleType.INSTALLER;
 333     }
 334 
 335     @Override
 336     public Collection<BundlerParamInfo<?>> getBundleParameters() {
 337         Collection<BundlerParamInfo<?>> results = new LinkedHashSet<>();
 338         results.addAll(LinuxAppBundler.getAppBundleParameters());
 339         results.addAll(getRpmBundleParameters());
 340         return results;
 341     }
 342 
 343     public static Collection<BundlerParamInfo<?>> getRpmBundleParameters() {
 344         return Arrays.asList(
 345                 APP_BUNDLER,
 346                 APP_NAME,
 347                 BUILD_ROOT,
 348                 BUNDLE_NAME,
 349                 CONFIG_ROOT,
 350                 CATEGORY,
 351                 DESCRIPTION,
 352                 ICON,
 353                 IMAGE_DIR,
 354                 IMAGES_ROOT,
 355                 LICENSE_FILES,
 356                 LICENSE_TYPE,
 357                 MENU_HINT,
 358                 SHORTCUT_HINT,
 359                 TITLE,
 360                 VENDOR,
 361                 VERSION
 362         );
 363     }
 364 
 365     @Override
 366     public File execute(Map<String, ? super Object> params, File outputParentDir) {
 367         return bundle(params, outputParentDir);
 368     }
 369 }


  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 com.sun.javafx.tools.packager.bundlers;
  27 
  28 import com.oracle.bundlers.AbstractBundler;
  29 import com.oracle.bundlers.BundlerParamInfo;
  30 import com.oracle.bundlers.StandardBundlerParam;
  31 import com.sun.javafx.tools.packager.Log;
  32 import com.sun.javafx.tools.resource.linux.LinuxResources;
  33 
  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 com.oracle.bundlers.StandardBundlerParam.*;
  46 
  47 public class LinuxRPMBundler extends AbstractBundler {
  48 
  49     private static final ResourceBundle I18N =
  50             ResourceBundle.getBundle("com.oracle.bundlers.linux.LinuxRpmBundler");
  51 
  52     public static final BundlerParamInfo<LinuxAppBundler> APP_BUNDLER = new StandardBundlerParam<>(
  53             I18N.getString("param.app-bundler.name"), 
  54             I18N.getString("param.app-bundler.description"),
  55             "linux.app.bundler",
  56             LinuxAppBundler.class, null, params -> new LinuxAppBundler(), false, null);
  57 
  58     public static final BundlerParamInfo<File> RPM_IMAGE_DIR = new StandardBundlerParam<>(
  59             I18N.getString("param.image-dir.name"), 
  60             I18N.getString("param.image-dir.description"),
  61             "linux.rpm.imageDir",
  62             File.class, null, params -> {
  63                 File imagesRoot = IMAGES_ROOT.fetchFrom(params);
  64                 if (!imagesRoot.exists()) imagesRoot.mkdirs();
  65                 return new File(imagesRoot, "linux-rpm.image");
  66             }, false, (s, p) -> new File(s));
  67 
  68     public static final BundlerParamInfo<File> CONFIG_ROOT = new StandardBundlerParam<>(
  69             I18N.getString("param.config-root.name"), 
  70             I18N.getString("param.config-root.description"),
  71             "configRoot",
  72             File.class, null, params ->  new File(BUILD_ROOT.fetchFrom(params), "linux"),
  73             false, (s, p) -> new File(s));
  74 
  75     public static final BundlerParamInfo<String> BUNDLE_NAME = new StandardBundlerParam<> (
  76             I18N.getString("param.bundle-name.name"), 
  77             I18N.getString("param.bundle-name.description"),
  78             "linux.bundleName",
  79             String.class, null, params -> {
  80                 String nm = APP_NAME.fetchFrom(params);
  81                 if (nm == null) return null;
  82         
  83                 //spaces are not allowed in RPM package names
  84                 nm = nm.replaceAll(" ", "");
  85                 return nm;
  86             }, false, (s, p) -> s);
  87 
  88     private final static String DEFAULT_ICON = "javalogo_white_32.png";
  89     private final static String DEFAULT_SPEC_TEMPLATE = "template.spec";
  90     private final static String DEFAULT_DESKTOP_FILE_TEMPLATE = "template.desktop";
  91     private final static String DEFAULT_INIT_SCRIPT_TEMPLATE = "template.rpm.init.script";
  92 
  93     public final static String TOOL_RPMBUILD = "rpmbuild";
  94     public final static double TOOL_RPMBUILD_MIN_VERSION = 4.0d;
  95 
  96     public LinuxRPMBundler() {
  97         super();
  98         baseResourceLoader = LinuxResources.class;
  99     }
 100 
 101     public static boolean testTool(String toolName, double minVersion) {
 102         try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); PrintStream ps = new PrintStream(baos)) {
 103             ProcessBuilder pb = new ProcessBuilder(
 104                     toolName,
 105                     "--version");
 106 
 107             IOUtils.exec(pb, Log.isDebug(), false, ps); //not interested in the output
 108 
 109             //TODO: Version is ignored; need to extract version string and compare!
 110             String content = new String(baos.toByteArray());
 111             Pattern pattern = Pattern.compile("RPM version (\\d+\\.\\d+)");
 112             Matcher matcher = pattern.matcher(content);
 113             if (matcher.find()) {
 114                 String v = matcher.group(1);
 115                 double version = new Double(v);
 116                 return minVersion <= version;
 117             } else {
 118                return false;
 119             }
 120         } catch (Exception e) {
 121             Log.verbose(MessageFormat.format(I18N.getString("message.test-for-tool"), toolName, e.getMessage()));
 122             return false;
 123         }
 124     }
 125 
 126     @Override
 127     public boolean validate(Map<String, ? super Object> p) throws UnsupportedPlatformException, ConfigException {
 128         try {
 129             if (p == null) throw new ConfigException(
 130                     I18N.getString("error.parameters-null"),
 131                     I18N.getString("error.parameters-null.advice"));
 132 
 133             //run basic validation to ensure requirements are met
 134             //we are not interested in return code, only possible exception
 135             APP_BUNDLER.fetchFrom(p).doValidate(p);
 136 
 137             //TODO: validate presense of required tools?
 138             if (!testTool(TOOL_RPMBUILD, TOOL_RPMBUILD_MIN_VERSION)){
 139                 throw new ConfigException(
 140                         I18N.getString(MessageFormat.format("error.cannot-find-rpmbuild", TOOL_RPMBUILD_MIN_VERSION)),
 141                         I18N.getString(MessageFormat.format("error.cannot-find-rpmbuild.advice", TOOL_RPMBUILD_MIN_VERSION)));
 142             }
 143 
 144             return true;
 145         } catch (RuntimeException re) {
 146             throw new ConfigException(re);
 147         }
 148     }
 149 
 150     private boolean prepareProto(Map<String, ? super Object> params) {
 151         File imageDir = RPM_IMAGE_DIR.fetchFrom(params);
 152         File appDir = APP_BUNDLER.fetchFrom(params).doBundle(params, imageDir, true);
 153         return appDir != null;
 154     }
 155 
 156     public File bundle(Map<String, ? super Object> p, File outdir) {
 157         File imageDir = RPM_IMAGE_DIR.fetchFrom(p);
 158         try {
 159 
 160             imageDir.mkdirs();
 161 
 162             boolean menuShortcut = MENU_HINT.fetchFrom(p);
 163             boolean desktopShortcut = SHORTCUT_HINT.fetchFrom(p);
 164             if (!menuShortcut && !desktopShortcut) {
 165                 //both can not be false - user will not find the app
 166                 Log.verbose(I18N.getString("message.one-shortcut-required"));
 167                 p.put(MENU_HINT.getID(), true);
 168             }
 169 
 170             if (prepareProto(p) && prepareProjectConfig(p)) {
 171                 return buildRPM(p, outdir);
 172             }
 173             return null;
 174         } catch (IOException ex) {
 175             ex.printStackTrace();
 176             return null;
 177         } finally {
 178             try {
 179                 if (VERBOSE.fetchFrom(p)) {
 180                     saveConfigFiles(p);
 181                 }
 182                 if (imageDir != null && !Log.isDebug()) {
 183                     IOUtils.deleteRecursive(imageDir);
 184                 } else if (imageDir != null) {
 185                     Log.info(MessageFormat.format(I18N.getString("message.debug-working-directory"), imageDir.getAbsolutePath()));
 186                 }
 187             } catch (FileNotFoundException ex) {
 188                 //noinspection ReturnInsideFinallyBlock
 189                 return null;
 190             }
 191         }
 192     }
 193 
 194     /*
 195      * set permissions with a string like "rwxr-xr-x"
 196      * 
 197      * This cannot be directly backport to 22u which is unfortunately built with 1.6
 198      */
 199     private void setPermissions(File file, String permissions) {
 200         Set<PosixFilePermission> filePermissions = PosixFilePermissions.fromString(permissions);
 201         try {
 202             if (file.exists()) {
 203                 Files.setPosixFilePermissions(file.toPath(), filePermissions);
 204             }
 205         } catch (IOException ex) {
 206             Logger.getLogger(LinuxDebBundler.class.getName()).log(Level.SEVERE, null, ex);
 207         }
 208     }
 209     
 210     protected void saveConfigFiles(Map<String, ? super Object> params) {
 211         try {
 212             File configRoot = CONFIG_ROOT.fetchFrom(params);
 213             if (getConfig_SpecFile(params).exists()) {
 214                 IOUtils.copyFile(getConfig_SpecFile(params),
 215                         new File(configRoot, getConfig_SpecFile(params).getName()));
 216             }
 217             if (getConfig_DesktopShortcutFile(params).exists()) {
 218                 IOUtils.copyFile(getConfig_DesktopShortcutFile(params),
 219                         new File(configRoot, getConfig_DesktopShortcutFile(params).getName()));
 220             }
 221             if (getConfig_IconFile(params).exists()) {
 222                 IOUtils.copyFile(getConfig_IconFile(params),
 223                         new File(configRoot, getConfig_IconFile(params).getName()));
 224             }
 225             if (SERVICE_HINT.fetchFrom(params)) {
 226                 if (getConfig_InitScriptFile(params).exists()) {
 227                     IOUtils.copyFile(getConfig_InitScriptFile(params),
 228                             new File(configRoot, getConfig_InitScriptFile(params).getName()));
 229                 }
 230             }
 231             Log.info(MessageFormat.format(I18N.getString("message.config-save-location"), configRoot.getAbsolutePath()));
 232         } catch (IOException ioe) {
 233             ioe.printStackTrace();
 234         }
 235     }
 236 
 237     @Override
 238     public String toString() {
 239         return getName();
 240     }
 241 
 242     private String getLicenseFileString(Map<String, ? super Object> params) {
 243         StringBuilder sb = new StringBuilder();
 244         for (String f: LICENSE_FILES.fetchFrom(params)) {
 245             if (sb.length() != 0) {
 246                 sb.append("\n");
 247             }
 248             sb.append("%doc /opt/");
 249             sb.append(BUNDLE_NAME.fetchFrom(params));
 250             sb.append("/app/");
 251             sb.append(f);
 252         }
 253         return sb.toString();
 254     }
 255 
 256     private boolean prepareProjectConfig(Map<String, ? super Object> params) throws IOException {
 257         Map<String, String> data = new HashMap<>();
 258 
 259         data.put("APPLICATION_NAME", BUNDLE_NAME.fetchFrom(params));
 260         data.put("APPLICATION_PACKAGE", BUNDLE_NAME.fetchFrom(params).toLowerCase());
 261         data.put("APPLICATION_VENDOR", VENDOR.fetchFrom(params));
 262         data.put("APPLICATION_VERSION", VERSION.fetchFrom(params));
 263         data.put("APPLICATION_LAUNCHER_FILENAME",
 264                 LinuxAppBundler.getLauncher(RPM_IMAGE_DIR.fetchFrom(params), params).getName());
 265         data.put("APPLICATION_DESKTOP_SHORTCUT", SHORTCUT_HINT.fetchFrom(params) ? "returnTrue" : "returnFalse");
 266         data.put("APPLICATION_MENU_SHORTCUT", MENU_HINT.fetchFrom(params) ? "returnTrue" : "returnFalse");
 267         data.put("DEPLOY_BUNDLE_CATEGORY", CATEGORY.fetchFrom(params)); //TODO rpm categories
 268         data.put("APPLICATION_DESCRIPTION", DESCRIPTION.fetchFrom(params));
 269         data.put("APPLICATION_SUMMARY", TITLE.fetchFrom(params));
 270         data.put("APPLICATION_LICENSE_TYPE", LICENSE_TYPE.fetchFrom(params));
 271         data.put("APPLICATION_LICENSE_FILE", getLicenseFileString(params));
 272         data.put("SERVICE_HINT", String.valueOf(SERVICE_HINT.fetchFrom(params)));
 273         data.put("START_ON_INSTALL", String.valueOf(START_ON_INSTALL.fetchFrom(params)));
 274         data.put("STOP_ON_UNINSTALL", String.valueOf(STOP_ON_UNINSTALL.fetchFrom(params)));
 275         data.put("RUN_AT_STARTUP", String.valueOf(RUN_AT_STARTUP.fetchFrom(params)));
 276 
 277         //prepare spec file
 278         Writer w = new BufferedWriter(new FileWriter(getConfig_SpecFile(params)));
 279         String content = preprocessTextResource(
 280                 LinuxAppBundler.LINUX_BUNDLER_PREFIX + getConfig_SpecFile(params).getName(),
 281                 I18N.getString("resource.rpm-spec-file"), DEFAULT_SPEC_TEMPLATE, data,
 282                 VERBOSE.fetchFrom(params));
 283         w.write(content);
 284         w.close();
 285 
 286         //prepare desktop shortcut
 287         w = new BufferedWriter(new FileWriter(getConfig_DesktopShortcutFile(params)));
 288         content = preprocessTextResource(
 289                 LinuxAppBundler.LINUX_BUNDLER_PREFIX + getConfig_DesktopShortcutFile(params).getName(),
 290                 I18N.getString("resource.menu-shortcut-descriptor"), DEFAULT_DESKTOP_FILE_TEMPLATE, data,
 291                 VERBOSE.fetchFrom(params));
 292         w.write(content);
 293         w.close();
 294 
 295         //prepare installer icon
 296         File iconTarget = getConfig_IconFile(params);
 297         File icon = ICON.fetchFrom(params);
 298         if (icon == null || !icon.exists()) {
 299             fetchResource(LinuxAppBundler.LINUX_BUNDLER_PREFIX + iconTarget.getName(),
 300                     I18N.getString("resource.menu-icon"),
 301                     DEFAULT_ICON,
 302                     iconTarget,
 303                     VERBOSE.fetchFrom(params));
 304         } else {
 305             fetchResource(LinuxAppBundler.LINUX_BUNDLER_PREFIX + iconTarget.getName(),
 306                     I18N.getString("resource.menu-icon"),
 307                     icon,
 308                     iconTarget,
 309                     VERBOSE.fetchFrom(params));
 310         }
 311 
 312         if (SERVICE_HINT.fetchFrom(params)) {
 313             //prepare init script
 314             w = new BufferedWriter(new FileWriter(getConfig_InitScriptFile(params)));
 315             content = preprocessTextResource(
 316                     LinuxAppBundler.LINUX_BUNDLER_PREFIX + getConfig_InitScriptFile(params).getName(),
 317                     I18N.getString("resource.rpm-init-script"), 
 318                     DEFAULT_INIT_SCRIPT_TEMPLATE, 
 319                     data,
 320                     VERBOSE.fetchFrom(params));
 321             w.write(content);
 322             w.close();
 323             setPermissions(getConfig_InitScriptFile(params), "rwxr-xr-x");
 324         }
 325 
 326         return true;
 327     }
 328 
 329     private File getConfig_DesktopShortcutFile(Map<String, ? super Object> params) {
 330         return new File(LinuxAppBundler.getLauncher(RPM_IMAGE_DIR.fetchFrom(params), params).getParentFile(),
 331                 BUNDLE_NAME.fetchFrom(params) + ".desktop");
 332     }
 333 
 334     private File getConfig_IconFile(Map<String, ? super Object> params) {
 335         return new File(LinuxAppBundler.getLauncher(RPM_IMAGE_DIR.fetchFrom(params), params).getParentFile(),
 336                 BUNDLE_NAME.fetchFrom(params) + ".png");
 337     }
 338 
 339     private File getConfig_InitScriptFile(Map<String, ? super Object> params) {
 340         return new File(LinuxAppBundler.getLauncher(RPM_IMAGE_DIR.fetchFrom(params), params).getParentFile(),
 341                 BUNDLE_NAME.fetchFrom(params) + ".init");
 342     }
 343 
 344     private File getConfig_SpecFile(Map<String, ? super Object> params) {
 345         return new File(RPM_IMAGE_DIR.fetchFrom(params),
 346                 BUNDLE_NAME.fetchFrom(params) + ".spec");
 347     }
 348 
 349     private File buildRPM(Map<String, ? super Object> params, File outdir) throws IOException {
 350         Log.verbose(MessageFormat.format(I18N.getString("message.outputting-bundle-location"), outdir.getAbsolutePath()));
 351 
 352         File broot = new File(BUILD_ROOT.fetchFrom(params), "rmpbuildroot");
 353 
 354         outdir.mkdirs();
 355 
 356         //run rpmbuild
 357         ProcessBuilder pb = new ProcessBuilder(
 358                 TOOL_RPMBUILD,
 359                 "-bb", getConfig_SpecFile(params).getAbsolutePath(),
 360 //                "--define", "%__jar_repack %{nil}",  //debug: improves build time (but will require unpack to install?)
 361                 "--define", "%_sourcedir "+ RPM_IMAGE_DIR.fetchFrom(params).getAbsolutePath(),
 362                 "--define", "%_rpmdir " + outdir.getAbsolutePath(), //save result to output dir
 363                 "--define", "%_topdir " + broot.getAbsolutePath() //do not use other system directories to build as current user
 364         );
 365         pb = pb.directory(RPM_IMAGE_DIR.fetchFrom(params));
 366         IOUtils.exec(pb, VERBOSE.fetchFrom(params));
 367 
 368         IOUtils.deleteRecursive(broot);
 369 
 370         Log.info(MessageFormat.format(I18N.getString("message.output-bundle-location"), outdir.getAbsolutePath()));
 371 
 372         // presume the result is the ".rpm" file with the newest modified time
 373         // not the best solution, but it is the most reliable
 374         File result = null;
 375         long lastModified = 0;
 376         File[] list = outdir.listFiles();
 377         if (list != null) {
 378             for (File f : list) {
 379                 if (f.getName().endsWith(".rpm") && f.lastModified() > lastModified) {
 380                     result = f;
 381                     lastModified = f.lastModified();
 382                 }
 383             }
 384         }
 385 
 386         return result;
 387     }
 388 
 389     @Override
 390     public String getName() {
 391         return I18N.getString("bundler.name");
 392     }
 393 
 394     @Override
 395     public String getDescription() {
 396         return I18N.getString("bundler.description");
 397     }
 398 
 399     @Override
 400     public String getID() {
 401         return "rpm";
 402     }
 403 
 404     @Override
 405     public BundleType getBundleType() {
 406         return BundleType.INSTALLER;
 407     }
 408 
 409     @Override
 410     public Collection<BundlerParamInfo<?>> getBundleParameters() {
 411         Collection<BundlerParamInfo<?>> results = new LinkedHashSet<>();
 412         results.addAll(LinuxAppBundler.getAppBundleParameters());
 413         results.addAll(getRpmBundleParameters());
 414         return results;
 415     }
 416 
 417     public static Collection<BundlerParamInfo<?>> getRpmBundleParameters() {
 418         return Arrays.asList(
 419                 APP_BUNDLER,
 420                 APP_NAME,
 421                 BUILD_ROOT,
 422                 BUNDLE_NAME,
 423                 CONFIG_ROOT,
 424                 CATEGORY,
 425                 DESCRIPTION,
 426                 ICON,
 427                 RPM_IMAGE_DIR,
 428                 IMAGES_ROOT,
 429                 LICENSE_FILES,
 430                 LICENSE_TYPE,
 431                 MENU_HINT,
 432                 SHORTCUT_HINT,
 433                 TITLE,
 434                 VENDOR,
 435                 VERSION
 436         );
 437     }
 438 
 439     @Override
 440     public File execute(Map<String, ? super Object> params, File outputParentDir) {
 441         return bundle(params, outputParentDir);
 442     }
 443 }