1 /*
   2  * Copyright (c) 2015, 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 package com.oracle.tools.packager;
  26 
  27 import java.io.File;
  28 import java.io.FileOutputStream;
  29 import java.io.IOException;
  30 import java.io.PrintStream;
  31 import java.nio.file.Files;
  32 import java.text.MessageFormat;
  33 import java.util.Arrays;
  34 import java.util.List;
  35 import java.util.Map;
  36 import java.util.ResourceBundle;
  37 import java.util.regex.Pattern;
  38 
  39 import static com.oracle.tools.packager.StandardBundlerParam.*;
  40 import static com.oracle.tools.packager.StandardBundlerParam.ARGUMENTS;
  41 
  42 /**
  43  * Common utility methods used by app image bundlers.
  44  */
  45 public abstract class AbstractImageBundler extends AbstractBundler {
  46 
  47     private static final ResourceBundle I18N =
  48             ResourceBundle.getBundle(AbstractImageBundler.class.getName());
  49 
  50     public static final String CFG_FORMAT_PROPERTIES="prop";
  51     public static final String CFG_FORMAT_INI="ini";
  52     
  53     public static final BundlerParamInfo<String> LAUNCHER_CFG_FORMAT =
  54             new StandardBundlerParam<>(
  55                     I18N.getString("param.launcher-cfg-format.name"),
  56                     I18N.getString("param.launcher-cfg-format.description"),
  57                     "launcher-cfg-format",
  58                     String.class,
  59                     params -> "ini",
  60                     (s, p) -> s);
  61 
  62     //helper method to test if required files are present in the runtime
  63     public void testRuntime(RelativeFileSet runtime, String[] file) throws ConfigException {
  64         if (runtime == null) {
  65             return; //null runtime is ok (request to use system)
  66         }
  67 
  68         Pattern[] weave = Arrays.stream(file).map(Pattern::compile).toArray(Pattern[]::new);
  69 
  70         if (!runtime.getIncludedFiles().stream().anyMatch(s ->
  71                         Arrays.stream(weave).anyMatch(pattern -> pattern.matcher(s).matches())
  72         )) {
  73             throw new ConfigException(
  74                     MessageFormat.format(I18N.getString("error.jre-missing-file"), Arrays.toString(file)),
  75                     I18N.getString("error.jre-missing-file.advice"));
  76         }
  77     }
  78 
  79     public void writeCfgFile(Map<String, ? super Object> params, File cfgFileName, String runtimeLocation) throws IOException {
  80         cfgFileName.delete();
  81 
  82         PrintStream out = new PrintStream(cfgFileName);
  83         
  84         out.println("[Application]");
  85         out.println("app.name=" + APP_NAME.fetchFrom(params));
  86         out.println("app.mainjar=" + MAIN_JAR.fetchFrom(params).getIncludedFiles().iterator().next());
  87         out.println("app.version=" + VERSION.fetchFrom(params));
  88         out.println("app.preferences.id=" + PREFERENCES_ID.fetchFrom(params));
  89         out.println("app.mainclass=" +
  90                 MAIN_CLASS.fetchFrom(params).replaceAll("\\.", "/"));
  91         out.println("app.classpath=" +
  92                 String.join(File.pathSeparator, CLASSPATH.fetchFrom(params).split("[ :;]")));
  93         out.println("app.runtime=" + runtimeLocation);
  94         out.println("app.identifier=" + IDENTIFIER.fetchFrom(params));
  95 
  96 
  97         out.println();
  98         out.println("[JVMOptions]");
  99         List<String> jvmargs = JVM_OPTIONS.fetchFrom(params);
 100         for (String arg : jvmargs) {
 101             out.println(arg);
 102         }
 103         Map<String, String> jvmProps = JVM_PROPERTIES.fetchFrom(params);
 104         for (Map.Entry<String, String> property : jvmProps.entrySet()) {
 105             out.println("-D" + property.getKey() + "=" + property.getValue());
 106         }
 107         String preloader = PRELOADER_CLASS.fetchFrom(params);
 108         if (preloader != null) {
 109             out.println("-Djavafx.preloader="+preloader);
 110         }
 111 
 112         
 113         out.println();
 114         out.println("[JVMUserOptions]");
 115         Map<String, String> overridableJVMOptions = USER_JVM_OPTIONS.fetchFrom(params);
 116         for (Map.Entry<String, String> arg: overridableJVMOptions.entrySet()) {
 117             if (arg.getKey() == null || arg.getValue() == null) {
 118                 Log.info(I18N.getString("message.jvm-user-arg-is-null"));
 119             } else {
 120                 out.println(arg.getKey().replaceAll("([\\=])", "\\$1") + "=" + arg.getValue());
 121             }
 122         }
 123 
 124         
 125         if (UNLOCK_COMMERCIAL_FEATURES.fetchFrom(params) && ENABLE_APP_CDS.fetchFrom(params)) {
 126             prepareAppCDS(params, out);
 127         }
 128         
 129         out.println();
 130         out.println("[ArgOptions]");
 131         List<String> args = ARGUMENTS.fetchFrom(params);
 132         for (String arg : args) {
 133             out.println(arg);
 134         }
 135 
 136         
 137         out.close();
 138     }
 139 
 140     protected abstract String getCacheLocation(Map<String, ? super Object> params);
 141     
 142     void prepareAppCDS(Map<String, ? super Object> params, PrintStream out) throws IOException {
 143         //TODO check 8u40 or later
 144 
 145         File tempDir = Files.createTempDirectory("javapackager").toFile();
 146         tempDir.deleteOnExit();
 147         File classList = new File(tempDir, APP_FS_NAME.fetchFrom(params)  + ".classlist");
 148 
 149         try (FileOutputStream fos = new FileOutputStream(classList);
 150              PrintStream ps = new PrintStream(fos)) {
 151             for (String className : APP_CDS_CLASS_ROOTS.fetchFrom(params)) {
 152                 String slashyName = className.replace(".", "/");
 153                 ps.println(slashyName);
 154             }
 155         }
 156         APP_RESOURCES_LIST.fetchFrom(params).add(new RelativeFileSet(classList.getParentFile(), Arrays.asList(classList)));
 157 
 158         out.println();
 159         out.println("[AppCDSJVMOptions]");
 160         out.println("-XX:+UnlockCommercialFeatures");
 161         out.print("-XX:SharedArchiveFile=");
 162         out.print(getCacheLocation(params));
 163         out.print(APP_FS_NAME.fetchFrom(params));
 164         out.println(".jpa");
 165         out.println("-Xshare:on");
 166         out.println("-XX:+UseAppCDS");
 167         if (Log.isDebug()) {
 168             out.println("-verbose:class");
 169             out.println("-XX:+TraceClassPaths");
 170             out.println("-XX:+UnlockDiagnosticVMOptions");
 171         }
 172         out.println("");
 173         
 174         out.println("[AppCDSGenerateCacheJVMOptions]");
 175         out.println("-XX:+UnlockCommercialFeatures");
 176         out.println("-Xshare:dump");
 177         out.println("-XX:+UseAppCDS");
 178         out.print("-XX:SharedArchiveFile=");
 179         out.print(getCacheLocation(params));
 180         out.print(APP_FS_NAME.fetchFrom(params));
 181         out.println(".jpa");
 182         out.println("-XX:SharedClassListFile=$PACKAGEDIR/" + APP_FS_NAME.fetchFrom(params) + ".classlist");
 183         if (Log.isDebug()) {
 184             out.println("-XX:+UnlockDiagnosticVMOptions");
 185         }
 186     }
 187 }