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 com.sun.javafx.tools.packager.bundlers.BundleParams; 28 29 import java.io.File; 30 import java.io.FileOutputStream; 31 import java.io.IOException; 32 import java.io.PrintStream; 33 import java.nio.file.Files; 34 import java.text.MessageFormat; 35 import java.util.Arrays; 36 import java.util.List; 37 import java.util.Map; 38 import java.util.ResourceBundle; 39 import java.util.regex.Matcher; 40 import java.util.regex.Pattern; 41 42 import static com.oracle.tools.packager.StandardBundlerParam.*; 43 import static com.oracle.tools.packager.StandardBundlerParam.ARGUMENTS; 44 45 /** 46 * Common utility methods used by app image bundlers. 47 */ 48 public abstract class AbstractImageBundler extends AbstractBundler { 49 50 private static final ResourceBundle I18N = 51 ResourceBundle.getBundle(AbstractImageBundler.class.getName()); 52 53 public static final String CFG_FORMAT_PROPERTIES="prop"; 54 public static final String CFG_FORMAT_INI="ini"; 55 56 public static final BundlerParamInfo<String> LAUNCHER_CFG_FORMAT = 57 new StandardBundlerParam<>( 58 I18N.getString("param.launcher-cfg-format.name"), 59 I18N.getString("param.launcher-cfg-format.description"), 60 "launcher-cfg-format", 61 String.class, 62 params -> "ini", 63 (s, p) -> s); 64 65 //helper method to test if required files are present in the runtime 66 public void testRuntime(RelativeFileSet runtime, String[] file) throws ConfigException { 67 if (runtime == null) { 68 return; //null runtime is ok (request to use system) 69 } 70 71 Pattern[] weave = Arrays.stream(file).map(Pattern::compile).toArray(Pattern[]::new); 72 73 if (!runtime.getIncludedFiles().stream().anyMatch(s -> 74 Arrays.stream(weave).anyMatch(pattern -> pattern.matcher(s).matches()) 75 )) { 76 throw new ConfigException( 77 MessageFormat.format(I18N.getString("error.jre-missing-file"), Arrays.toString(file)), 78 I18N.getString("error.jre-missing-file.advice")); 79 } 80 } 81 82 public void imageBundleValidation(Map<String, ? super Object> p) throws ConfigException { 83 StandardBundlerParam.validateMainClassInfoFromAppResources(p); 84 85 Map<String, String> userJvmOptions = USER_JVM_OPTIONS.fetchFrom(p); 86 if (userJvmOptions != null) { 87 for (Map.Entry<String, String> entry : userJvmOptions.entrySet()) { 88 if (entry.getValue() == null || entry.getValue().isEmpty()) { 89 throw new ConfigException( 90 MessageFormat.format(I18N.getString("error.empty-user-jvm-option-value"), entry.getKey()), 91 I18N.getString("error.empty-user-jvm-option-value.advice")); 92 } 93 } 94 } 95 96 if (MAIN_JAR.fetchFrom(p) == null) { 97 throw new ConfigException( 98 I18N.getString("error.no-application-jar"), 99 I18N.getString("error.no-application-jar.advice")); 100 } 101 102 extractRuntimeFlags(p); 103 104 if (ENABLE_APP_CDS.fetchFrom(p)) { 105 if (UNLOCK_COMMERCIAL_FEATURES.fetchFrom(p)) { 106 if (p.containsKey(BundleParams.PARAM_RUNTIME) 107 && (p.get(BundleParams.PARAM_RUNTIME) == null)) 108 { 109 throw new ConfigException( 110 I18N.getString("error.app-cds-requires-runtime"), 111 I18N.getString("error.app-cds-requires-runtime.advice")); 112 } 113 Object majorV = p.get(".runtime.version.major"); 114 Object minorV = p.get(".runtime.version.minor"); 115 if (majorV != null && minorV != null) { 116 try { 117 int major = Integer.parseInt(majorV.toString()); 118 int minor = Integer.parseInt(minorV.toString()); 119 if ((major < 8) || (major == 8 && minor < 40)) { 120 throw new ConfigException( 121 I18N.getString("error.app-cds-bad-version"), 122 I18N.getString("error.app-cds-bad-version.advice")); 123 } 124 } catch (NumberFormatException nfe) { 125 //maybe log a failure to check versions? 126 } 127 } 128 } else { 129 throw new ConfigException( 130 I18N.getString("error.app-cds-no-commercial-unlock"), 131 I18N.getString("error.app-cds-no-commercial-unlock.advice")); 132 } 133 } 134 135 } 136 137 public void writeCfgFile(Map<String, ? super Object> params, File cfgFileName, String runtimeLocation) throws IOException { 138 cfgFileName.delete(); 139 140 boolean appCDEnabled = UNLOCK_COMMERCIAL_FEATURES.fetchFrom(params) && ENABLE_APP_CDS.fetchFrom(params); 141 String appCDSCacheMode = APP_CDS_CACHE_MODE.fetchFrom(params); 142 143 PrintStream out = new PrintStream(cfgFileName); 144 145 out.println("[Application]"); 146 out.println("app.name=" + APP_NAME.fetchFrom(params)); 147 out.println("app.mainjar=" + MAIN_JAR.fetchFrom(params).getIncludedFiles().iterator().next()); 148 out.println("app.version=" + VERSION.fetchFrom(params)); 149 out.println("app.preferences.id=" + PREFERENCES_ID.fetchFrom(params)); 150 out.println("app.mainclass=" + 151 MAIN_CLASS.fetchFrom(params).replaceAll("\\.", "/")); 152 out.println("app.classpath=" + 153 String.join(File.pathSeparator, CLASSPATH.fetchFrom(params).split("[ :;]"))); 154 out.println("app.runtime=" + runtimeLocation); 155 out.println("app.identifier=" + IDENTIFIER.fetchFrom(params)); 156 if (appCDEnabled) { 157 out.println("app.appcds.cache=" + appCDSCacheMode.split("\\+")[0]); 158 } 159 160 161 out.println(); 162 out.println("[JVMOptions]"); 163 List<String> jvmargs = JVM_OPTIONS.fetchFrom(params); 164 for (String arg : jvmargs) { 165 out.println(arg); 166 } 167 Map<String, String> jvmProps = JVM_PROPERTIES.fetchFrom(params); 168 for (Map.Entry<String, String> property : jvmProps.entrySet()) { 169 out.println("-D" + property.getKey() + "=" + property.getValue()); 170 } 171 String preloader = PRELOADER_CLASS.fetchFrom(params); 172 if (preloader != null) { 173 out.println("-Djavafx.preloader="+preloader); 174 } 175 176 177 out.println(); 178 out.println("[JVMUserOptions]"); 179 Map<String, String> overridableJVMOptions = USER_JVM_OPTIONS.fetchFrom(params); 180 for (Map.Entry<String, String> arg: overridableJVMOptions.entrySet()) { 181 if (arg.getKey() == null || arg.getValue() == null) { 182 Log.info(I18N.getString("message.jvm-user-arg-is-null")); 183 } else { 184 out.println(arg.getKey().replaceAll("([\\=])", "\\\\$1") + "=" + arg.getValue()); 185 } 186 } 187 188 if (appCDEnabled) { 189 prepareAppCDS(params, out); 190 } 191 192 out.println(); 193 out.println("[ArgOptions]"); 194 List<String> args = ARGUMENTS.fetchFrom(params); 195 for (String arg : args) { 196 if (arg.endsWith("=") && (arg.indexOf("=") == arg.lastIndexOf("="))) { 197 out.print(arg.substring(0, arg.length() - 1)); 198 out.println("\\="); 199 } else { 200 out.println(arg); 201 } 202 } 203 204 205 out.close(); 206 } 207 208 protected abstract String getCacheLocation(Map<String, ? super Object> params); 209 210 void prepareAppCDS(Map<String, ? super Object> params, PrintStream out) throws IOException { 211 //TODO check 8u40 or later 212 213 File tempDir = Files.createTempDirectory("javapackager").toFile(); 214 tempDir.deleteOnExit(); 215 File classList = new File(tempDir, APP_FS_NAME.fetchFrom(params) + ".classlist"); 216 217 try (FileOutputStream fos = new FileOutputStream(classList); 218 PrintStream ps = new PrintStream(fos)) { 219 for (String className : APP_CDS_CLASS_ROOTS.fetchFrom(params)) { 220 String slashyName = className.replace(".", "/"); 221 ps.println(slashyName); 222 } 223 } 224 APP_RESOURCES_LIST.fetchFrom(params).add(new RelativeFileSet(classList.getParentFile(), Arrays.asList(classList))); 225 226 out.println(); 227 out.println("[AppCDSJVMOptions]"); 228 out.println("-XX:+UnlockCommercialFeatures"); 229 out.print("-XX:SharedArchiveFile="); 230 out.print(getCacheLocation(params)); 231 out.print(APP_FS_NAME.fetchFrom(params)); 232 out.println(".jpa"); 233 out.println("-Xshare:auto"); 234 out.println("-XX:+UseAppCDS"); 235 if (Log.isDebug()) { 236 out.println("-verbose:class"); 237 out.println("-XX:+TraceClassPaths"); 238 out.println("-XX:+UnlockDiagnosticVMOptions"); 239 } 240 out.println(""); 241 242 out.println("[AppCDSGenerateCacheJVMOptions]"); 243 out.println("-XX:+UnlockCommercialFeatures"); 244 out.println("-Xshare:dump"); 245 out.println("-XX:+UseAppCDS"); 246 out.print("-XX:SharedArchiveFile="); 247 out.print(getCacheLocation(params)); 248 out.print(APP_FS_NAME.fetchFrom(params)); 249 out.println(".jpa"); 250 out.println("-XX:SharedClassListFile=$PACKAGEDIR/" + APP_FS_NAME.fetchFrom(params) + ".classlist"); 251 if (Log.isDebug()) { 252 out.println("-XX:+UnlockDiagnosticVMOptions"); 253 } 254 } 255 256 abstract public void extractRuntimeFlags(Map<String, ? super Object> params); 257 258 public static void extractFlagsFromVersion(Map<String, ? super Object> params, String versionOutput) { 259 Pattern bitArchPattern = Pattern.compile("(\\d*)[- ]?[bB]it"); 260 Matcher matcher = bitArchPattern.matcher(versionOutput); 261 if (matcher.find()) { 262 params.put(".runtime.bit-arch", matcher.group(1)); 263 } else { 264 // presume 32 bit on no match 265 params.put(".runtime.bit-arch", "32"); 266 } 267 268 Pattern oldVersionMatcher = Pattern.compile("java version \"((\\d+.(\\d+).\\d+)(_(\\d+)))?(-(.*))?\""); 269 matcher = oldVersionMatcher.matcher(versionOutput); 270 if (matcher.find()) { 271 params.put(".runtime.version", matcher.group(1)); 272 params.put(".runtime.version.release", matcher.group(2)); 273 params.put(".runtime.version.major", matcher.group(3)); 274 params.put(".runtime.version.update", matcher.group(5)); 275 params.put(".runtime.version.minor", matcher.group(5)); 276 params.put(".runtime.version.security", matcher.group(5)); 277 params.put(".runtime.version.patch", "0"); 278 params.put(".runtime.version.modifiers", matcher.group(7)); 279 } else { 280 Pattern newVersionMatcher = Pattern.compile("java version \"((\\d+).(\\d+).(\\d+).(\\d+))(-(.*))?(\\+[^\"]*)?\""); 281 matcher = newVersionMatcher.matcher(versionOutput); 282 if (matcher.find()) { 283 params.put(".runtime.version", matcher.group(1)); 284 params.put(".runtime.version.release", matcher.group(1)); 285 params.put(".runtime.version.major", matcher.group(2)); 286 params.put(".runtime.version.update", matcher.group(3)); 287 params.put(".runtime.version.minor", matcher.group(3)); 288 params.put(".runtime.version.security", matcher.group(4)); 289 params.put(".runtime.version.patch", matcher.group(5)); 290 params.put(".runtime.version.modifiers", matcher.group(7)); 291 } else { 292 params.put(".runtime.version", ""); 293 params.put(".runtime.version.release", ""); 294 params.put(".runtime.version.major", ""); 295 params.put(".runtime.version.update", ""); 296 params.put(".runtime.version.minor", ""); 297 params.put(".runtime.version.security", ""); 298 params.put(".runtime.version.patch", ""); 299 params.put(".runtime.version.modifiers", ""); 300 } 301 } 302 } 303 304 305 }