1 /*
   2  * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package jdk.incubator.jpackage.internal;
  27 
  28 import java.io.ByteArrayOutputStream;
  29 import java.io.File;
  30 import java.io.FileInputStream;
  31 import java.io.IOException;
  32 import java.io.InputStream;
  33 import java.io.PrintStream;
  34 import java.nio.file.Files;
  35 import java.nio.file.Path;
  36 import java.text.MessageFormat;
  37 import java.util.List;
  38 import java.util.Map;
  39 import java.util.ResourceBundle;
  40 import java.util.ArrayList;
  41 
  42 import jdk.incubator.jpackage.internal.resources.ResourceLocator;
  43 
  44 import static jdk.incubator.jpackage.internal.StandardBundlerParam.*;
  45 
  46 /*
  47  * AbstractAppImageBuilder
  48  *     This is sub-classed by each of the platform dependent AppImageBuilder
  49  * classes, and contains resource processing code common to all platforms.
  50  */
  51 
  52 public abstract class AbstractAppImageBuilder {
  53 
  54     private static final ResourceBundle I18N = ResourceBundle.getBundle(
  55             "jdk.incubator.jpackage.internal.resources.MainResources");
  56 
  57     private final Path root;
  58 
  59     public AbstractAppImageBuilder(Map<String, Object> unused, Path root) {
  60         this.root = root;
  61     }
  62 
  63     public InputStream getResourceAsStream(String name) {
  64         return ResourceLocator.class.getResourceAsStream(name);
  65     }
  66 
  67     public abstract void prepareApplicationFiles(
  68             Map<String, ? super Object> params) throws IOException;
  69     public abstract void prepareJreFiles(
  70             Map<String, ? super Object> params) throws IOException;
  71     public abstract Path getAppDir();
  72     public abstract Path getAppModsDir();
  73 
  74     public Path getRuntimeRoot() {
  75         return this.root;
  76     }
  77 
  78     protected void copyEntry(Path appDir, File srcdir, String fname)
  79             throws IOException {
  80         Path dest = appDir.resolve(fname);
  81         Files.createDirectories(dest.getParent());
  82         File src = new File(srcdir, fname);
  83         if (src.isDirectory()) {
  84             IOUtils.copyRecursive(src.toPath(), dest);
  85         } else {
  86             Files.copy(src.toPath(), dest);
  87         }
  88     }
  89 
  90     public void writeCfgFile(Map<String, ? super Object> params,
  91             File cfgFileName) throws IOException {
  92         cfgFileName.getParentFile().mkdirs();
  93         cfgFileName.delete();
  94         File mainJar = JLinkBundlerHelper.getMainJar(params);
  95         ModFile.ModType mainJarType = ModFile.ModType.Unknown;
  96 
  97         if (mainJar != null) {
  98             mainJarType = new ModFile(mainJar).getModType();
  99         }
 100 
 101         String mainModule = StandardBundlerParam.MODULE.fetchFrom(params);
 102 
 103         try (PrintStream out = new PrintStream(cfgFileName)) {
 104 
 105             out.println("[Application]");
 106             out.println("app.name=" + APP_NAME.fetchFrom(params));
 107             out.println("app.version=" + VERSION.fetchFrom(params));
 108             out.println("app.runtime=" + getCfgRuntimeDir());
 109             out.println("app.identifier=" + IDENTIFIER.fetchFrom(params));
 110             out.println("app.classpath="
 111                     + getCfgClassPath(CLASSPATH.fetchFrom(params)));
 112 
 113             // The main app is required to be a jar, modular or unnamed.
 114             if (mainModule != null &&
 115                     (mainJarType == ModFile.ModType.Unknown ||
 116                     mainJarType == ModFile.ModType.ModularJar)) {
 117                 out.println("app.mainmodule=" + mainModule);
 118             } else {
 119                 String mainClass =
 120                         StandardBundlerParam.MAIN_CLASS.fetchFrom(params);
 121                 // If the app is contained in an unnamed jar then launch it the
 122                 // legacy way and the main class string must be
 123                 // of the format com/foo/Main
 124                 if (mainJar != null) {
 125                     out.println("app.mainjar=" + getCfgAppDir()
 126                             + mainJar.toPath().getFileName().toString());
 127                 }
 128                 if (mainClass != null) {
 129                     out.println("app.mainclass="
 130                             + mainClass.replace("\\", "/"));
 131                 }
 132             }
 133 
 134             out.println();
 135             out.println("[JavaOptions]");
 136             List<String> jvmargs = JAVA_OPTIONS.fetchFrom(params);
 137             for (String arg : jvmargs) {
 138                 out.println(arg);
 139             }
 140             Path modsDir = getAppModsDir();
 141 
 142             if (modsDir != null && modsDir.toFile().exists()) {
 143                 out.println("--module-path");
 144                 out.println(getCfgAppDir().replace("\\","/") + "mods");
 145             }
 146 
 147             out.println();
 148             out.println("[ArgOptions]");
 149             List<String> args = ARGUMENTS.fetchFrom(params);
 150             for (String arg : args) {
 151                 if (arg.endsWith("=") &&
 152                         (arg.indexOf("=") == arg.lastIndexOf("="))) {
 153                     out.print(arg.substring(0, arg.length() - 1));
 154                     out.println("\\=");
 155                 } else {
 156                     out.println(arg);
 157                 }
 158             }
 159         }
 160     }
 161 
 162     File getRuntimeImageDir(File runtimeImageTop) {
 163         return runtimeImageTop;
 164     }
 165 
 166     protected String getCfgAppDir() {
 167         return "$ROOTDIR" + File.separator
 168                 + getAppDir().getFileName() + File.separator;
 169     }
 170 
 171     protected String getCfgRuntimeDir() {
 172         return "$ROOTDIR" + File.separator + "runtime";
 173     }
 174 
 175     String getCfgClassPath(String classpath) {
 176         String cfgAppDir = getCfgAppDir();
 177 
 178         StringBuilder sb = new StringBuilder();
 179         for (String path : classpath.split("[:;]")) {
 180             if (path.length() > 0) {
 181                 sb.append(cfgAppDir);
 182                 sb.append(path);
 183                 sb.append(File.pathSeparator);
 184             }
 185         }
 186         if (sb.length() > 0) {
 187             sb.deleteCharAt(sb.length() - 1);
 188         }
 189         return sb.toString();
 190     }
 191 }