1 package jdk.packager.builders; 2 3 import com.oracle.tools.packager.JLinkBundlerHelper; 4 import com.oracle.tools.packager.RelativeFileSet; 5 import jdk.tools.jlink.builder.*; 6 import jdk.tools.jlink.plugin.Pool; 7 8 import com.oracle.tools.packager.Log; 9 10 import java.io.ByteArrayOutputStream; 11 import java.io.File; 12 import java.io.FileInputStream; 13 import java.io.FileOutputStream; 14 import java.io.IOException; 15 import java.io.InputStream; 16 import java.io.PrintStream; 17 import java.nio.file.Files; 18 import java.nio.file.Path; 19 import java.text.MessageFormat; 20 import java.util.Arrays; 21 import java.util.List; 22 import java.util.Map; 23 import java.util.ResourceBundle; 24 import java.util.Set; 25 26 import static com.oracle.tools.packager.StandardBundlerParam.*; 27 import static com.oracle.tools.packager.StandardBundlerParam.ARGUMENTS; 28 import static com.oracle.tools.packager.StandardBundlerParam.USER_JVM_OPTIONS; 29 30 /** 31 * 32 * Created by dferrin on 9/1/15. 33 */ 34 public abstract class AbstractAppImageBuilder extends DefaultImageBuilder { 35 36 private static final ResourceBundle I18N = 37 ResourceBundle.getBundle(AbstractAppImageBuilder.class.getName()); 38 39 //do not use file separator - 40 // we use it for classpath lookup and there / are not platform specific 41 public final static String BUNDLER_PREFIX = "package/"; 42 43 public AbstractAppImageBuilder(Map<String, Object> properties, Path root) throws IOException { 44 super(true, root); 45 } 46 47 abstract protected void prepareApplicationFiles(Pool files, Set<String> modules) throws IOException; 48 49 abstract protected InputStream getResourceAsStream(String name); 50 51 protected InputStream locateResource(String publicName, String category, 52 String defaultName, File customFile, 53 boolean verbose, File publicRoot) throws IOException { 54 InputStream is = null; 55 boolean customFromClasspath = false; 56 boolean customFromFile = false; 57 if (publicName != null) { 58 if (publicRoot != null) { 59 File publicResource = new File(publicRoot, publicName); 60 if (publicResource.exists() && publicResource.isFile()) { 61 is = new FileInputStream(publicResource); 62 } 63 } else { 64 is = getResourceAsStream(publicName); 65 } 66 customFromClasspath = (is != null); 67 } 68 if (is == null && customFile != null) { 69 is = new FileInputStream(customFile); 70 customFromFile = (is != null); 71 } 72 if (is == null && defaultName != null) { 73 is = getResourceAsStream(defaultName); 74 } 75 String msg = null; 76 if (customFromClasspath) { 77 msg = MessageFormat.format(I18N.getString("message.using-custom-resource-from-classpath"), category == null ? "" : "[" + category + "] ", publicName); 78 } else if (customFromFile) { 79 msg = MessageFormat.format(I18N.getString("message.using-custom-resource-from-file"), category == null ? "" : "[" + category + "] ", customFile.getAbsoluteFile()); 80 } else if (is != null) { 81 msg = MessageFormat.format(I18N.getString("message.using-default-resource-from-classpath"), category == null ? "" : "[" + category + "] ", publicName); 82 } else { 83 msg = MessageFormat.format(I18N.getString("message.using-default-resource"), category == null ? "" : "[" + category + "] ", publicName); 84 } 85 if (verbose) { 86 Log.info(msg); 87 } 88 return is; 89 } 90 91 92 protected String preprocessTextResource(String publicName, String category, 93 String defaultName, Map<String, String> pairs, 94 boolean verbose, File publicRoot) throws IOException { 95 InputStream inp = locateResource(publicName, category, defaultName, null, verbose, publicRoot); 96 if (inp == null) { 97 throw new RuntimeException("Module corrupt? No "+defaultName+" resource!"); 98 } 99 100 try (InputStream is = inp) { 101 //read fully into memory 102 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 103 byte[] buffer = new byte[1024]; 104 int length; 105 while ((length = is.read(buffer)) != -1) { 106 baos.write(buffer, 0, length); 107 } 108 109 //substitute 110 String result = new String(baos.toByteArray()); 111 for (Map.Entry<String, String> e : pairs.entrySet()) { 112 if (e.getValue() != null) { 113 result = result.replace(e.getKey(), e.getValue()); 114 } 115 } 116 return result; 117 } 118 } 119 120 public void writeCfgFile(Map<String, ? super Object> params, File cfgFileName, String runtimeLocation) throws IOException { 121 cfgFileName.delete(); 122 123 boolean appCDEnabled = UNLOCK_COMMERCIAL_FEATURES.fetchFrom(params) && ENABLE_APP_CDS.fetchFrom(params); 124 String appCDSCacheMode = APP_CDS_CACHE_MODE.fetchFrom(params); 125 126 PrintStream out = new PrintStream(cfgFileName); 127 128 out.println("[Application]"); 129 out.println("app.name=" + APP_NAME.fetchFrom(params)); 130 out.println("app.mainjar=" + MAIN_JAR.fetchFrom(params).getIncludedFiles().iterator().next()); 131 out.println("app.version=" + VERSION.fetchFrom(params)); 132 out.println("app.preferences.id=" + PREFERENCES_ID.fetchFrom(params)); 133 out.println("app.mainclass=" + 134 MAIN_CLASS.fetchFrom(params).replaceAll("\\.", "/")); 135 out.println("app.classpath=" + 136 String.join(File.pathSeparator, CLASSPATH.fetchFrom(params).split("[ :;]"))); 137 out.println("app.modulepath=" + 138 String.join(File.pathSeparator, JLinkBundlerHelper.JDK_MODULE_PATH.fetchFrom(params))); 139 out.println("app.runtime=" + runtimeLocation); 140 out.println("app.identifier=" + IDENTIFIER.fetchFrom(params)); 141 if (appCDEnabled) { 142 out.println("app.appcds.cache=" + appCDSCacheMode.split("\\+")[0]); 143 } 144 145 146 out.println(); 147 out.println("[JVMOptions]"); 148 List<String> jvmargs = JVM_OPTIONS.fetchFrom(params); 149 for (String arg : jvmargs) { 150 out.println(arg); 151 } 152 Map<String, String> jvmProps = JVM_PROPERTIES.fetchFrom(params); 153 for (Map.Entry<String, String> property : jvmProps.entrySet()) { 154 out.println("-D" + property.getKey() + "=" + property.getValue()); 155 } 156 String preloader = PRELOADER_CLASS.fetchFrom(params); 157 if (preloader != null) { 158 out.println("-Djavafx.preloader="+preloader); 159 } 160 161 162 out.println(); 163 out.println("[JVMUserOptions]"); 164 Map<String, String> overridableJVMOptions = USER_JVM_OPTIONS.fetchFrom(params); 165 for (Map.Entry<String, String> arg: overridableJVMOptions.entrySet()) { 166 if (arg.getKey() == null || arg.getValue() == null) { 167 Log.info(I18N.getString("message.jvm-user-arg-is-null")); 168 } else { 169 out.println(arg.getKey().replaceAll("([\\=])", "\\\\$1") + "=" + arg.getValue()); 170 } 171 } 172 173 if (appCDEnabled) { 174 prepareAppCDS(params, out); 175 } 176 177 out.println(); 178 out.println("[ArgOptions]"); 179 List<String> args = ARGUMENTS.fetchFrom(params); 180 for (String arg : args) { 181 if (arg.endsWith("=") && (arg.indexOf("=") == arg.lastIndexOf("="))) { 182 out.print(arg.substring(0, arg.length() - 1)); 183 out.println("\\="); 184 } else { 185 out.println(arg); 186 } 187 } 188 189 190 out.close(); 191 } 192 193 protected abstract String getCacheLocation(Map<String, ? super Object> params); 194 195 void prepareAppCDS(Map<String, ? super Object> params, PrintStream out) throws IOException { 196 //TODO check 8u40 or later 197 198 File tempDir = Files.createTempDirectory("javapackager").toFile(); 199 tempDir.deleteOnExit(); 200 File classList = new File(tempDir, APP_FS_NAME.fetchFrom(params) + ".classlist"); 201 202 try (FileOutputStream fos = new FileOutputStream(classList); 203 PrintStream ps = new PrintStream(fos)) { 204 for (String className : APP_CDS_CLASS_ROOTS.fetchFrom(params)) { 205 String slashyName = className.replace(".", "/"); 206 ps.println(slashyName); 207 } 208 } 209 APP_RESOURCES_LIST.fetchFrom(params).add(new RelativeFileSet(classList.getParentFile(), Arrays.asList(classList))); 210 211 out.println(); 212 out.println("[AppCDSJVMOptions]"); 213 out.println("-XX:+UnlockCommercialFeatures"); 214 out.print("-XX:SharedArchiveFile="); 215 out.print(getCacheLocation(params)); 216 out.print(APP_FS_NAME.fetchFrom(params)); 217 out.println(".jpa"); 218 out.println("-Xshare:auto"); 219 out.println("-XX:+UseAppCDS"); 220 if (Log.isDebug()) { 221 out.println("-verbose:class"); 222 out.println("-XX:+TraceClassPaths"); 223 out.println("-XX:+UnlockDiagnosticVMOptions"); 224 } 225 out.println(""); 226 227 out.println("[AppCDSGenerateCacheJVMOptions]"); 228 out.println("-XX:+UnlockCommercialFeatures"); 229 out.println("-Xshare:dump"); 230 out.println("-XX:+UseAppCDS"); 231 out.print("-XX:SharedArchiveFile="); 232 out.print(getCacheLocation(params)); 233 out.print(APP_FS_NAME.fetchFrom(params)); 234 out.println(".jpa"); 235 out.println("-XX:SharedClassListFile=$PACKAGEDIR/" + APP_FS_NAME.fetchFrom(params) + ".classlist"); 236 if (Log.isDebug()) { 237 out.println("-XX:+UnlockDiagnosticVMOptions"); 238 } 239 } 240 241 242 }