1 /* 2 * Copyright (c) 2015, 2018, 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.packager.internal.builders; 27 28 import jdk.packager.internal.IOUtils; 29 import jdk.packager.internal.Log; 30 import jdk.packager.internal.StandardBundlerParam; 31 import jdk.packager.internal.JLinkBundlerHelper; 32 import jdk.packager.internal.ModFile; 33 34 import java.io.ByteArrayOutputStream; 35 import java.io.File; 36 import java.io.FileInputStream; 37 import java.io.IOException; 38 import java.io.InputStream; 39 import java.io.PrintStream; 40 import java.nio.file.Files; 41 import java.nio.file.Path; 42 import java.text.MessageFormat; 43 import java.util.List; 44 import java.util.Map; 45 import java.util.ResourceBundle; 46 import java.util.ArrayList; 47 48 import static jdk.packager.internal.StandardBundlerParam.*; 49 import static jdk.packager.internal.StandardBundlerParam.ARGUMENTS; 50 51 public abstract class AbstractAppImageBuilder { 52 53 private static final ResourceBundle I18N = 54 ResourceBundle.getBundle( 55 "jdk.packager.internal.resources.builders.AbstractAppImageBuilder"); 56 57 //do not use file separator - 58 // we use it for classpath lookup and there / are not platform specific 59 public final static String BUNDLER_PREFIX = "package/"; 60 61 private final Map<String, Object> properties; 62 private final Path root; 63 protected List<String> excludeFileList = new ArrayList<>(); 64 65 public AbstractAppImageBuilder(Map<String, Object> properties, 66 Path root) throws IOException { 67 this.properties = properties; 68 this.root = root; 69 excludeFileList.add(".*\\.diz"); 70 } 71 72 public abstract InputStream getResourceAsStream(String name); 73 public abstract void prepareApplicationFiles() throws IOException; 74 public abstract void prepareServerJreFiles() throws IOException; 75 76 public Map<String, Object> getProperties() { 77 return this.properties; 78 } 79 80 public Path getRoot() { 81 return this.root; 82 } 83 84 public String getExcludeFileList() { 85 return String.join(",", excludeFileList); 86 } 87 88 protected void copyEntry(Path appDir, File srcdir, String fname) 89 throws IOException { 90 Path dest = appDir.resolve(fname); 91 Files.createDirectories(dest.getParent()); 92 File src = new File(srcdir, fname); 93 if (src.isDirectory()) { 94 IOUtils.copyRecursive(src.toPath(), dest); 95 } else { 96 Files.copy(src.toPath(), dest); 97 } 98 } 99 100 protected InputStream locateResource(String publicName, String category, 101 String defaultName, File customFile, 102 boolean verbose, File publicRoot) throws IOException { 103 InputStream is = null; 104 boolean customFromClasspath = false; 105 boolean customFromFile = false; 106 if (publicName != null) { 107 if (publicRoot != null) { 108 File publicResource = new File(publicRoot, publicName); 109 if (publicResource.exists() && publicResource.isFile()) { 110 is = new FileInputStream(publicResource); 111 } 112 } else { 113 is = getResourceAsStream(publicName); 114 } 115 customFromClasspath = (is != null); 116 } 117 if (is == null && customFile != null) { 118 is = new FileInputStream(customFile); 119 customFromFile = (is != null); 120 } 121 if (is == null && defaultName != null) { 122 is = getResourceAsStream(defaultName); 123 } 124 if (verbose) { 125 String msg = null; 126 if (customFromClasspath) { 127 msg = MessageFormat.format(I18N.getString( 128 "message.using-custom-resource-from-classpath"), 129 category == null ? "" : "[" + category + "] ", publicName); 130 } else if (customFromFile) { 131 msg = MessageFormat.format(I18N.getString( 132 "message.using-custom-resource-from-file"), 133 category == null ? "" : "[" + category + "] ", 134 customFile.getAbsoluteFile()); 135 } else if (is != null) { 136 msg = MessageFormat.format(I18N.getString( 137 "message.using-default-resource-from-classpath"), 138 category == null ? "" : "[" + category + "] ", publicName); 139 } else { 140 msg = MessageFormat.format(I18N.getString( 141 "message.using-default-resource"), 142 category == null ? "" : "[" + category + "] ", publicName); 143 } 144 if (msg != null) { 145 Log.info(msg); 146 } 147 } 148 return is; 149 } 150 151 152 protected String preprocessTextResource(String publicName, String category, 153 String defaultName, Map<String, String> pairs, 154 boolean verbose, File publicRoot) throws IOException { 155 InputStream inp = locateResource(publicName, category, 156 defaultName, null, verbose, publicRoot); 157 if (inp == null) { 158 throw new RuntimeException( 159 "Module corrupt? No "+defaultName+" resource!"); 160 } 161 162 try (InputStream is = inp) { 163 //read fully into memory 164 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 165 byte[] buffer = new byte[1024]; 166 int length; 167 while ((length = is.read(buffer)) != -1) { 168 baos.write(buffer, 0, length); 169 } 170 171 //substitute 172 String result = new String(baos.toByteArray()); 173 for (Map.Entry<String, String> e : pairs.entrySet()) { 174 if (e.getValue() != null) { 175 result = result.replace(e.getKey(), e.getValue()); 176 } 177 } 178 return result; 179 } 180 } 181 182 public void writeCfgFile(Map<String, ? super Object> params, 183 File cfgFileName, String runtimeLocation) throws IOException { 184 cfgFileName.delete(); 185 186 File mainJar = JLinkBundlerHelper.getMainJar(params); 187 ModFile.ModType mainJarType = ModFile.ModType.Unknown; 188 189 if (mainJar != null) { 190 mainJarType = new ModFile(mainJar).getModType(); 191 } 192 193 String mainModule = StandardBundlerParam.MODULE.fetchFrom(params); 194 195 PrintStream out = new PrintStream(cfgFileName); 196 197 out.println("[Application]"); 198 out.println("app.name=" + APP_NAME.fetchFrom(params)); 199 out.println("app.version=" + VERSION.fetchFrom(params)); 200 out.println("app.preferences.id=" + PREFERENCES_ID.fetchFrom(params)); 201 out.println("app.runtime=" + runtimeLocation); 202 out.println("app.identifier=" + IDENTIFIER.fetchFrom(params)); 203 out.println("app.classpath=" + String.join(File.pathSeparator, 204 CLASSPATH.fetchFrom(params).split("[ :;]"))); 205 out.println("app.application.instance=" + 206 (SINGLETON.fetchFrom(params) ? "single" : "multiple")); 207 208 // The main app is required to be a jar, modular or unnamed. 209 if (mainModule != null && 210 (mainJarType == ModFile.ModType.Unknown || 211 mainJarType == ModFile.ModType.ModularJar)) { 212 out.println("app.mainmodule=" + mainModule); 213 } else { 214 String mainClass = JLinkBundlerHelper.getMainClass(params); 215 // If the app is contained in an unnamed jar then launch it the 216 // legacy way and the main class string must be 217 // of the format com/foo/Main 218 if (mainJar != null) { 219 out.println("app.mainjar=" 220 + mainJar.toPath().getFileName().toString()); 221 } 222 if (mainClass != null) { 223 out.println("app.mainclass=" 224 + mainClass.replaceAll("\\.", "/")); 225 } 226 } 227 228 String version = JLinkBundlerHelper.getJDKVersion(params); 229 230 if (!version.isEmpty()) { 231 out.println("app.java.version=" + version); 232 } 233 234 out.println("packager.java.version=" 235 + System.getProperty("java.version")); 236 237 Integer port = JLinkBundlerHelper.DEBUG.fetchFrom(params); 238 239 if (port != null) { 240 out.println( 241 "app.debug=-agentlib:jdwp=transport=dt_socket," 242 + "server=y,suspend=y,address=localhost:" 243 + port); 244 } 245 246 out.println(); 247 out.println("[JVMOptions]"); 248 List<String> jvmargs = JVM_OPTIONS.fetchFrom(params); 249 for (String arg : jvmargs) { 250 out.println(arg); 251 } 252 Map<String, String> jvmProps = JVM_PROPERTIES.fetchFrom(params); 253 for (Map.Entry<String, String> property : jvmProps.entrySet()) { 254 out.println("-D" + property.getKey() + "=" + property.getValue()); 255 } 256 257 out.println(); 258 out.println("[ArgOptions]"); 259 List<String> args = ARGUMENTS.fetchFrom(params); 260 for (String arg : args) { 261 if (arg.endsWith("=") && 262 (arg.indexOf("=") == arg.lastIndexOf("="))) { 263 out.print(arg.substring(0, arg.length() - 1)); 264 out.println("\\="); 265 } else { 266 out.println(arg); 267 } 268 } 269 270 271 out.close(); 272 } 273 274 public String getPlatformSpecificModulesFile() { 275 return null; 276 } 277 278 }