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.File; 29 import java.io.IOException; 30 import java.io.InputStream; 31 import java.io.PrintStream; 32 import java.nio.file.Files; 33 import java.nio.file.Path; 34 import java.util.HashMap; 35 import java.util.List; 36 import java.util.Map; 37 import java.util.ResourceBundle; 38 import static jdk.incubator.jpackage.internal.OverridableResource.createResource; 39 import static jdk.incubator.jpackage.internal.StandardBundlerParam.*; 40 41 import jdk.incubator.jpackage.internal.resources.ResourceLocator; 42 43 44 /* 45 * AbstractAppImageBuilder 46 * This is sub-classed by each of the platform dependent AppImageBuilder 47 * classes, and contains resource processing code common to all platforms. 48 */ 49 50 public abstract class AbstractAppImageBuilder { 51 52 private static final ResourceBundle I18N = ResourceBundle.getBundle( 53 "jdk.incubator.jpackage.internal.resources.MainResources"); 54 55 private final Path root; 56 57 public AbstractAppImageBuilder(Map<String, Object> unused, Path root) { 58 this.root = root; 59 } 60 61 public InputStream getResourceAsStream(String name) { 62 return ResourceLocator.class.getResourceAsStream(name); 63 } 64 65 public abstract void prepareApplicationFiles( 66 Map<String, ? super Object> params) throws IOException; 67 public abstract void prepareJreFiles( 68 Map<String, ? super Object> params) throws IOException; 69 public abstract Path getAppDir(); 70 public abstract Path getAppModsDir(); 71 72 public Path getRuntimeRoot() { 73 return this.root; 74 } 75 76 protected void copyEntry(Path appDir, File srcdir, String fname) 77 throws IOException { 78 Path dest = appDir.resolve(fname); 79 Files.createDirectories(dest.getParent()); 80 File src = new File(srcdir, fname); 81 if (src.isDirectory()) { 82 IOUtils.copyRecursive(src.toPath(), dest); 83 } else { 84 Files.copy(src.toPath(), dest); 85 } 86 } 87 88 public void writeCfgFile(Map<String, ? super Object> params, 89 File cfgFileName) throws IOException { 90 cfgFileName.getParentFile().mkdirs(); 91 cfgFileName.delete(); 92 File mainJar = JLinkBundlerHelper.getMainJar(params); 93 ModFile.ModType mainJarType = ModFile.ModType.Unknown; 94 95 if (mainJar != null) { 96 mainJarType = new ModFile(mainJar).getModType(); 97 } 98 99 String mainModule = StandardBundlerParam.MODULE.fetchFrom(params); 100 101 try (PrintStream out = new PrintStream(cfgFileName)) { 102 103 out.println("[Application]"); 104 out.println("app.name=" + APP_NAME.fetchFrom(params)); 105 out.println("app.version=" + VERSION.fetchFrom(params)); 106 out.println("app.runtime=" + getCfgRuntimeDir()); 107 out.println("app.identifier=" + IDENTIFIER.fetchFrom(params)); 108 out.println("app.classpath=" 109 + getCfgClassPath(CLASSPATH.fetchFrom(params))); 110 111 // The main app is required to be a jar, modular or unnamed. 112 if (mainModule != null && 113 (mainJarType == ModFile.ModType.Unknown || 114 mainJarType == ModFile.ModType.ModularJar)) { 115 out.println("app.mainmodule=" + mainModule); 116 } else { 117 String mainClass = 118 StandardBundlerParam.MAIN_CLASS.fetchFrom(params); 119 // If the app is contained in an unnamed jar then launch it the 120 // legacy way and the main class string must be 121 // of the format com/foo/Main 122 if (mainJar != null) { 123 out.println("app.mainjar=" + getCfgAppDir() 124 + mainJar.toPath().getFileName().toString()); 125 } 126 if (mainClass != null) { 127 out.println("app.mainclass=" 128 + mainClass.replace("\\", "/")); 129 } 130 } 131 132 out.println(); 133 out.println("[JavaOptions]"); 134 List<String> jvmargs = JAVA_OPTIONS.fetchFrom(params); 135 for (String arg : jvmargs) { 136 out.println(arg); 137 } 138 Path modsDir = getAppModsDir(); 139 140 if (modsDir != null && modsDir.toFile().exists()) { 141 out.println("--module-path"); 142 out.println(getCfgAppDir().replace("\\","/") + "mods"); 143 } 144 145 out.println(); 146 out.println("[ArgOptions]"); 147 List<String> args = ARGUMENTS.fetchFrom(params); 148 for (String arg : args) { 149 if (arg.endsWith("=") && 150 (arg.indexOf("=") == arg.lastIndexOf("="))) { 151 out.print(arg.substring(0, arg.length() - 1)); 152 out.println("\\="); 153 } else { 154 out.println(arg); 155 } 156 } 157 } 158 } 159 160 File getRuntimeImageDir(File runtimeImageTop) { 161 return runtimeImageTop; 162 } 163 164 protected String getCfgAppDir() { 165 return "$ROOTDIR" + File.separator 166 + getAppDir().getFileName() + File.separator; 167 } 168 169 protected String getCfgRuntimeDir() { 170 return "$ROOTDIR" + File.separator + "runtime"; 171 } 172 173 String getCfgClassPath(String classpath) { 174 String cfgAppDir = getCfgAppDir(); 175 176 StringBuilder sb = new StringBuilder(); 177 for (String path : classpath.split("[:;]")) { 178 if (path.length() > 0) { 179 sb.append(cfgAppDir); 180 sb.append(path); 181 sb.append(File.pathSeparator); 182 } 183 } 184 if (sb.length() > 0) { 185 sb.deleteCharAt(sb.length() - 1); 186 } 187 return sb.toString(); 188 } 189 190 public static OverridableResource createIconResource(String defaultIconName, 191 BundlerParamInfo<File> iconParam, Map<String, ? super Object> params, 192 Map<String, ? super Object> mainParams) throws IOException { 193 194 if (mainParams != null) { 195 params = AddLauncherArguments.merge(mainParams, params, ICON.getID(), 196 iconParam.getID()); 197 } 198 199 final String resourcePublicName = APP_NAME.fetchFrom(params) 200 + IOUtils.getSuffix(Path.of(defaultIconName)); 201 202 IconType iconType = getLauncherIconType(params); 203 if (iconType == IconType.NoIcon) { 204 return null; 205 } 206 207 OverridableResource resource = createResource(defaultIconName, params) 208 .setCategory("icon") 209 .setExternal(iconParam.fetchFrom(params)) 210 .setPublicName(resourcePublicName); 211 212 if (iconType == IconType.DefaultOrResourceDirIcon && mainParams != null) { 213 // No icon explicitly configured for this launcher. 214 // Dry-run resource creation to figure out its source. 215 final Path nullPath = null; 216 if (resource.saveToFile(nullPath) 217 != OverridableResource.Source.ResourceDir) { 218 // No icon in resource dir for this launcher, inherit icon 219 // configured for the main launcher. 220 resource = createIconResource(defaultIconName, iconParam, 221 mainParams, null).setLogPublicName(resourcePublicName); 222 } 223 } 224 225 return resource; 226 } 227 228 private enum IconType { DefaultOrResourceDirIcon, CustomIcon, NoIcon }; 229 230 private static IconType getLauncherIconType(Map<String, ? super Object> params) { 231 File launcherIcon = ICON.fetchFrom(params); 232 if (launcherIcon == null) { 233 return IconType.DefaultOrResourceDirIcon; 234 } 235 236 if (launcherIcon.getName().isEmpty()) { 237 return IconType.NoIcon; 238 } 239 240 return IconType.CustomIcon; 241 } 242 }