1 /*
   2  * Copyright (c) 2012, 2016, 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 com.oracle.tools.packager.linux;
  27 
  28 import com.oracle.tools.packager.AbstractImageBundler;
  29 import com.oracle.tools.packager.BundlerParamInfo;
  30 import com.oracle.tools.packager.ConfigException;
  31 import com.oracle.tools.packager.IOUtils;
  32 import com.oracle.tools.packager.JreUtils;
  33 import com.oracle.tools.packager.JreUtils.Rule;
  34 import com.oracle.tools.packager.Log;
  35 import com.oracle.tools.packager.Platform;
  36 import com.oracle.tools.packager.RelativeFileSet;
  37 import com.oracle.tools.packager.StandardBundlerParam;
  38 import com.oracle.tools.packager.UnsupportedPlatformException;
  39 import com.sun.javafx.tools.packager.bundlers.BundleParams;
  40 import jdk.packager.builders.linux.LinuxAppImageBuilder;
  41 
  42 import jdk.packager.internal.JLinkBundlerHelper;
  43 
  44 import java.io.File;
  45 import java.io.IOException;
  46 import java.net.MalformedURLException;
  47 import java.net.URL;
  48 import java.text.MessageFormat;
  49 import java.util.Arrays;
  50 import java.util.Collection;
  51 import java.util.Map;
  52 import java.util.ResourceBundle;
  53 
  54 import static com.oracle.tools.packager.StandardBundlerParam.*;
  55 import jdk.packager.builders.AbstractAppImageBuilder;
  56 
  57 public class LinuxAppBundler extends AbstractImageBundler {
  58 
  59     private static final ResourceBundle I18N =
  60             ResourceBundle.getBundle(LinuxAppBundler.class.getName());
  61 
  62     protected static final String LINUX_BUNDLER_PREFIX =
  63             BUNDLER_PREFIX + "linux" + File.separator;
  64     private static final String EXECUTABLE_NAME = "JavaAppLauncher";
  65 
  66     public static final BundlerParamInfo<File> ICON_PNG = new StandardBundlerParam<>(
  67             I18N.getString("param.icon-png.name"),
  68             I18N.getString("param.icon-png.description"),
  69             "icon.png",
  70             File.class,
  71             params -> {
  72                 File f = ICON.fetchFrom(params);
  73                 if (f != null && !f.getName().toLowerCase().endsWith(".png")) {
  74                     Log.info(MessageFormat.format(I18N.getString("message.icon-not-png"), f));
  75                     return null;
  76                 }
  77                 return f;
  78             },
  79             (s, p) -> new File(s));
  80 
  81     public static final BundlerParamInfo<URL> RAW_EXECUTABLE_URL = new StandardBundlerParam<>(
  82             I18N.getString("param.raw-executable-url.name"),
  83             I18N.getString("param.raw-executable-url.description"),
  84             "linux.launcher.url",
  85             URL.class,
  86             params -> LinuxResources.class.getResource(EXECUTABLE_NAME),
  87             (s, p) -> {
  88                 try {
  89                     return new URL(s);
  90                 } catch (MalformedURLException e) {
  91                     Log.info(e.toString());
  92                     return null;
  93                 }
  94             });
  95 
  96     //Subsetting of JRE is restricted.
  97     //JRE README defines what is allowed to strip:
  98     //   http://www.oracle.com/technetwork/java/javase/jre-8-readme-2095710.html
  99     //
 100     public static final BundlerParamInfo<Rule[]> LINUX_JRE_RULES = new StandardBundlerParam<>(
 101             "",
 102             "",
 103             ".linux.runtime.rules",
 104             Rule[].class,
 105             params -> new Rule[]{
 106                     Rule.prefixNeg("/bin"),
 107                     Rule.prefixNeg("/plugin"),
 108                     //Rule.prefixNeg("/lib/ext"), //need some of jars there for https to work
 109                     Rule.suffix("deploy.jar"), //take deploy.jar
 110                     Rule.prefixNeg("/lib/deploy"),
 111                     Rule.prefixNeg("/lib/desktop"),
 112                     Rule.substrNeg("libnpjp2.so")
 113             },
 114             (s, p) ->  null
 115     );
 116 
 117     public static final BundlerParamInfo<RelativeFileSet> LINUX_RUNTIME = new StandardBundlerParam<>(
 118             I18N.getString("param.runtime.name"),
 119             I18N.getString("param.runtime.description"),
 120             BundleParams.PARAM_RUNTIME,
 121             RelativeFileSet.class,
 122             params -> JreUtils.extractJreAsRelativeFileSet(System.getProperty("java.home"),
 123                     LINUX_JRE_RULES.fetchFrom(params)),
 124             (s, p) -> JreUtils.extractJreAsRelativeFileSet(s, LINUX_JRE_RULES.fetchFrom(p))
 125     );
 126 
 127     @Override
 128     public boolean validate(Map<String, ? super Object> p) throws UnsupportedPlatformException, ConfigException {
 129         try {
 130             if (p == null) throw new ConfigException(
 131                     I18N.getString("error.parameters-null"),
 132                     I18N.getString("error.parameters-null.advice"));
 133 
 134             return doValidate(p);
 135         } catch (RuntimeException re) {
 136             if (re.getCause() instanceof ConfigException) {
 137                 throw (ConfigException) re.getCause();
 138             } else {
 139                 throw new ConfigException(re);
 140             }
 141         }
 142     }
 143 
 144     //used by chained bundlers to reuse validation logic
 145     boolean doValidate(Map<String, ? super Object> p) throws UnsupportedPlatformException, ConfigException {
 146         if (Platform.getPlatform() != Platform.LINUX) {
 147             throw new UnsupportedPlatformException();
 148         }
 149 
 150         imageBundleValidation(p);
 151 
 152         if (RAW_EXECUTABLE_URL.fetchFrom(p) == null) {
 153             throw new ConfigException(
 154                     I18N.getString("error.no-linux-resources"),
 155                     I18N.getString("error.no-linux-resources.advice"));
 156         }
 157 
 158         return true;
 159     }
 160 
 161     //it is static for the sake of sharing with "installer" bundlers
 162     // that may skip calls to validate/bundle in this class!
 163     public static File getRootDir(File outDir, Map<String, ? super Object> p) {
 164         return new File(outDir, APP_FS_NAME.fetchFrom(p));
 165     }
 166 
 167     public static String getLauncherCfgName(Map<String, ? super Object> p) {
 168         return "app/" + APP_FS_NAME.fetchFrom(p) +".cfg";
 169     }
 170 
 171     File doBundle(Map<String, ? super Object> p, File outputDirectory, boolean dependentTask) {
 172         try {
 173             if (!outputDirectory.isDirectory() && !outputDirectory.mkdirs()) {
 174                 throw new RuntimeException(MessageFormat.format(I18N.getString("error.cannot-create-output-dir"), outputDirectory.getAbsolutePath()));
 175             }
 176             if (!outputDirectory.canWrite()) {
 177                 throw new RuntimeException(MessageFormat.format(I18N.getString("error.cannot-write-to-output-dir"), outputDirectory.getAbsolutePath()));
 178             }
 179 
 180             // Create directory structure
 181             File rootDirectory = getRootDir(outputDirectory, p);
 182             IOUtils.deleteRecursive(rootDirectory);
 183             rootDirectory.mkdirs();
 184 
 185             if (!dependentTask) {
 186                 Log.info(MessageFormat.format(I18N.getString("message.creating-bundle-location"), rootDirectory.getAbsolutePath()));
 187             }
 188 
 189             if (!p.containsKey(JLinkBundlerHelper.JLINK_BUILDER.getID())) {
 190                 p.put(JLinkBundlerHelper.JLINK_BUILDER.getID(), "linuxapp-image-builder");
 191             }
 192 
 193             AbstractAppImageBuilder appBuilder = new LinuxAppImageBuilder(p, outputDirectory.toPath());
 194             JLinkBundlerHelper.execute(p, appBuilder);
 195 
 196             return rootDirectory;
 197         } catch (IOException ex) {
 198             Log.info("Exception: "+ex);
 199             Log.debug(ex);
 200             return null;
 201         } catch (Exception ex) {
 202             Log.info("Exception: "+ex);
 203             Log.debug(ex);
 204             return null;
 205         }
 206     }
 207 
 208     @Override
 209     public String getName() {
 210         return I18N.getString("bundler.name");
 211     }
 212 
 213     @Override
 214     public String getDescription() {
 215         return I18N.getString("bundler.description");
 216     }
 217 
 218     @Override
 219     public String getID() {
 220         return "linux.app";
 221     }
 222 
 223     @Override
 224     public String getBundleType() {
 225         return "IMAGE";
 226     }
 227 
 228     @Override
 229     public Collection<BundlerParamInfo<?>> getBundleParameters() {
 230         return getAppBundleParameters();
 231     }
 232 
 233     public static Collection<BundlerParamInfo<?>> getAppBundleParameters() {
 234         return Arrays.asList(
 235                 APP_NAME,
 236                 APP_RESOURCES,
 237                 // APP_RESOURCES_LIST, // ??
 238                 ARGUMENTS,
 239                 CLASSPATH,
 240                 JVM_OPTIONS,
 241                 JVM_PROPERTIES,
 242                 LINUX_RUNTIME,
 243                 MAIN_CLASS,
 244                 MAIN_JAR,
 245                 PREFERENCES_ID,
 246                 PRELOADER_CLASS,
 247                 USER_JVM_OPTIONS,
 248                 VERSION
 249         );
 250     }
 251 
 252     @Override
 253     public File execute(Map<String, ? super Object> params, File outputParentDir) {
 254         return doBundle(params, outputParentDir, false);
 255     }
 256 }