1 /* 2 * Copyright (c) 2012, 2014, 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 com.sun.javafx.tools.packager.bundlers; 27 28 import com.sun.javafx.tools.packager.bundlers.Bundler.BundleType; 29 import com.oracle.bundlers.BundlerParamInfo; 30 import com.sun.javafx.tools.packager.Log; 31 import com.sun.javafx.tools.packager.PackagerLib; 32 33 import java.io.File; 34 import java.io.IOException; 35 import java.util.*; 36 import java.util.jar.Attributes; 37 import java.util.jar.JarFile; 38 import java.util.jar.Manifest; 39 40 import static com.oracle.bundlers.StandardBundlerParam.*; 41 42 public class BundleParams { 43 44 final protected Map<String, ? super Object> params; 45 46 public static final String PARAM_RUNTIME = "runtime"; // RelativeFileSet 47 public static final String PARAM_APP_RESOURCES = "appResources"; // RelativeFileSet 48 public static final String PARAM_TYPE = "type"; // BundlerType 49 public static final String PARAM_BUNDLE_FORMAT = "bundleFormat"; // String 50 public static final String PARAM_ICON = "icon"; // String 51 52 /* Name of bundle file and native launcher */ 53 public static final String PARAM_NAME = "name"; // String 54 55 /* application vendor, used by most of the bundlers */ 56 public static final String PARAM_VENDOR = "vendor"; // String 57 58 /* email name and email, only used for debian */ 59 public static final String PARAM_EMAIL = "email"; // String 60 61 /* Copyright. Used on Mac */ 62 public static final String PARAM_COPYRIGHT = "copyright"; // String 63 64 /* GUID on windows for MSI, CFBundleIdentifier on Mac 65 If not compatible with requirements then bundler either do not bundle 66 or autogenerate */ 67 public static final String PARAM_IDENTIFIER = "identifier"; // String 68 69 /* shortcut preferences */ 70 public static final String PARAM_SHORTCUT = "shortcutHint"; // boolean 71 public static final String PARAM_MENU = "menuHint"; // boolean 72 73 /* Application version. Format may differ for different bundlers */ 74 public static final String PARAM_VERSION = "appVersion"; // String 75 /* Application category. Used at least on Mac/Linux. Value is platform specific */ 76 public static final String PARAM_CATEGORY = "applicationCategory"; // String 77 78 /* Optional short application */ 79 public static final String PARAM_TITLE = "title"; // String 80 81 /* Optional application description. Used by MSI and on Linux */ 82 public static final String PARAM_DESCRIPTION = "description"; // String 83 84 /* License type. Needed on Linux (rpm) */ 85 public static final String PARAM_LICENSE_TYPE = "licenseType"; // String 86 87 /* File(s) with license. Format is OS/bundler specific */ 88 public static final String PARAM_LICENSE_FILE = "licenseFile"; // List<String> 89 90 /* user or system level install. 91 null means "default" */ 92 public static final String PARAM_SYSTEM_WIDE = "systemWide"; // Boolean 93 94 /* service/daemon install. 95 null means "default" */ 96 public static final String PARAM_SERVICE_HINT = "serviceHint"; // Boolean 97 98 99 /* Main application class. Not used directly but used to derive default values */ 100 public static final String PARAM_APPLICATION_CLASS = "applicationClass"; // String 101 102 //list of jvm args (in theory string can contain spaces and need to be escaped 103 private List<String> jvmargs = new LinkedList<>(); 104 //list of jvm args (in theory string can contain spaces and need to be escaped 105 private Map<String, String> jvmUserArgs = new HashMap<>(); 106 107 //list of jvm properties (can also be passed as VM args 108 private Map<String, String> jvmProperties = new HashMap<>(); 109 110 /** 111 * create a new bundle with all default values 112 */ 113 public BundleParams() { 114 params = new HashMap<>(); 115 } 116 117 /** 118 * Create a bundle params with a copy of the params 119 * @param params map of initial parameters to be copied in. 120 */ 121 public BundleParams(Map<String, ?> params) { 122 this.params = new HashMap<>(params); 123 } 124 125 public void addAllBundleParams(Map<String, ? super Object> p) { 126 params.putAll(p); 127 } 128 129 public <C> C fetchParam(BundlerParamInfo<C> paramInfo) { 130 return paramInfo.fetchFrom(params); 131 } 132 133 @SuppressWarnings("unchecked") 134 public <C> C fetchParamWithDefault(Class<C> klass, C defaultValue, String... keys) { 135 for (String key : keys) { 136 Object o = params.get(key); 137 if (klass.isInstance(o)) { 138 return (C) o; 139 } else if (params.containsKey(keys) && o == null) { 140 return null; 141 // } else if (o != null) { 142 // TODO log an error. 143 } 144 } 145 return defaultValue; 146 } 147 148 public <C> C fetchParam(Class<C> klass, String... keys) { 149 return fetchParamWithDefault(klass, null, keys); 150 } 151 152 //NOTE: we do not care about application parameters here 153 // as they will be embeded into jar file manifest and 154 // java launcher will take care of them! 155 156 public Map<String, ? super Object> getBundleParamsAsMap() { 157 return new HashMap<>(params); 158 } 159 160 public void setJvmargs(List<String> jvmargs) { 161 this.jvmargs = jvmargs; 162 } 163 164 public void setJvmUserArgs(Map<String, String> userArgs) { 165 this.jvmUserArgs = userArgs; 166 } 167 168 public void setJvmProperties(Map<String, String> jvmProperties) { 169 this.jvmProperties = jvmProperties; 170 } 171 172 public List<String> getAllJvmOptions() { 173 List<String> all = new LinkedList<>(); 174 all.addAll(jvmargs); 175 for(String k: jvmProperties.keySet()) { 176 all.add("-D"+k+"="+jvmProperties.get(k)); //TODO: We are not escaping values here... 177 } 178 return all; 179 } 180 181 public Map<String, String> getAllJvmUserOptions() { 182 Map<String, String> all = new HashMap<>(); 183 all.putAll(jvmUserArgs); 184 return all; 185 } 186 187 public String getApplicationID() { 188 return fetchParam(IDENTIFIER); 189 } 190 191 public String getPreferencesID() { 192 return fetchParam(PREFERENCES_ID); 193 } 194 195 public String getTitle() { 196 return fetchParam(TITLE); 197 } 198 199 public void setTitle(String title) { 200 putUnlessNull(PARAM_TITLE, title); 201 } 202 203 public String getApplicationClass() { 204 return fetchParam(MAIN_CLASS); 205 } 206 207 public void setApplicationClass(String applicationClass) { 208 putUnlessNull(PARAM_APPLICATION_CLASS, applicationClass); 209 } 210 211 public String getAppVersion() { 212 return fetchParam(VERSION); 213 } 214 215 public void setAppVersion(String version) { 216 putUnlessNull(PARAM_VERSION, version); 217 } 218 219 public String getDescription() { 220 return fetchParam(DESCRIPTION); 221 } 222 223 public void setDescription(String s) { 224 putUnlessNull(PARAM_DESCRIPTION, s); 225 } 226 227 public String getLicenseType() { 228 return fetchParam(LICENSE_TYPE); 229 } 230 231 public void setLicenseType(String version) { 232 putUnlessNull(PARAM_LICENSE_TYPE, version); 233 } 234 235 //path is relative to the application root 236 public void addLicenseFile(String path) { 237 List<String> licenseFiles = fetchParam(LICENSE_FILE); 238 if (licenseFiles == null || licenseFiles.isEmpty()) { 239 licenseFiles = new ArrayList<>(); 240 params.put(PARAM_LICENSE_FILE, licenseFiles); 241 } 242 licenseFiles.add(path); 243 } 244 245 public Boolean getSystemWide() { 246 return fetchParam(SYSTEM_WIDE); 247 } 248 249 public void setSystemWide(Boolean b) { 250 putUnlessNull(PARAM_SYSTEM_WIDE, b); 251 } 252 253 public void setServiceHint(Boolean b) { 254 putUnlessNull(PARAM_SERVICE_HINT, b); 255 } 256 257 public RelativeFileSet getRuntime() { 258 return fetchParam(RUNTIME); 259 } 260 261 public boolean isShortcutHint() { 262 return fetchParam(SHORTCUT_HINT); 263 } 264 265 public void setShortcutHint(Boolean v) { 266 putUnlessNull(PARAM_SHORTCUT, v); 267 } 268 269 public boolean isMenuHint() { 270 return fetchParam(MENU_HINT); 271 } 272 273 public void setMenuHint(Boolean v) { 274 putUnlessNull(PARAM_MENU, v); 275 } 276 277 public String getName() { 278 return fetchParam(APP_NAME); 279 } 280 281 public void setName(String name) { 282 putUnlessNull(PARAM_NAME, name); 283 } 284 285 public BundleType getType() { 286 return fetchParam(BundleType.class, PARAM_TYPE); 287 } 288 289 public void setType(BundleType type) { 290 putUnlessNull(PARAM_TYPE, type); 291 } 292 293 public String getBundleFormat() { 294 return fetchParam(String.class, PARAM_BUNDLE_FORMAT); 295 } 296 297 public void setBundleFormat(String t) { 298 putUnlessNull(PARAM_BUNDLE_FORMAT, t); 299 } 300 301 public boolean getVerbose() { 302 return fetchParam(VERBOSE); 303 } 304 305 public void setVerbose(Boolean verbose) { 306 putUnlessNull(VERBOSE.getID(), verbose); 307 } 308 309 public List<String> getLicenseFile() { 310 return fetchParam(LICENSE_FILE); 311 } 312 313 public List<String> getJvmargs() { 314 return jvmargs; 315 } 316 317 //Validation approach: 318 // - JRE marker (rt.jar) 319 // - FX marker (jfxrt.jar) 320 // - JDK marker (tools.jar) 321 private static boolean checkJDKRoot(File jdkRoot) { 322 File rtJar = new File(jdkRoot, "jre/lib/rt.jar"); 323 if (!rtJar.exists()) { 324 Log.verbose("rt.jar is not found at " + rtJar.getAbsolutePath()); 325 return false; 326 } 327 328 File jfxJar = new File(jdkRoot, "jre/lib/ext/jfxrt.jar"); 329 if (!jfxJar.exists()) { 330 //Try again with new location 331 jfxJar = new File(jdkRoot, "jre/lib/jfxrt.jar"); 332 if (!jfxJar.exists()) { 333 Log.verbose("jfxrt.jar is not found at " + jfxJar.getAbsolutePath()); 334 return false; 335 } 336 } 337 338 339 File toolsJar = new File(jdkRoot, "lib/tools.jar"); 340 if (!toolsJar.exists()) { 341 Log.verbose("tools.jar is not found at " + toolsJar.getAbsolutePath()); 342 return false; 343 } 344 345 return true; 346 } 347 348 //Depending on platform and user input we may get different "references" 349 //Should support 350 // - java.home 351 // - reference to JDK install folder 352 // - should NOT support JRE dir 353 //Note: input could be null (then we asked to use system JRE) 354 // or it must be valid directory 355 //Returns null on validation failure. Returns jre root if ok. 356 static File validateRuntimeLocation(File javaHome) { 357 if (javaHome == null) { 358 return null; 359 } 360 File jdkRoot; 361 362 boolean isMac = System.getProperty("os.name").toLowerCase().contains("os x"); 363 364 File rtJar = new File(javaHome, "lib/rt.jar"); 365 if (rtJar.exists()) { //must be "java.home" case 366 //i.e. we are in JRE folder 367 jdkRoot = javaHome.getParentFile(); 368 } else { //expect it to be root of JDK installation folder 369 //On Mac it could be jdk/ or jdk/Contents/Home 370 //Norm to jdk/Contents/Home for validation 371 if (isMac) { 372 File f = new File(javaHome, "Contents/Home"); 373 if (f.exists() && f.isDirectory()) { 374 javaHome = f; 375 } 376 } 377 jdkRoot = javaHome; 378 } 379 380 if (!checkJDKRoot(jdkRoot)) { 381 throw new RuntimeException( 382 "Can not find JDK artifacts in specified location: " 383 + javaHome.getAbsolutePath()); 384 } 385 386 return new File(jdkRoot, "jre"); 387 } 388 389 //select subset of given runtime using predefined rules 390 public void setRuntime(File baseDir) { 391 baseDir = validateRuntimeLocation(baseDir); 392 393 //mistake or explicit intent to use system runtime 394 if (baseDir == null) { 395 Log.verbose("No Java runtime to embed. Package will need system Java."); 396 params.put(PARAM_RUNTIME, null); 397 return; 398 } 399 doSetRuntime(baseDir); 400 } 401 402 //input dir "jdk/jre" (i.e. jre folder in the jdk) 403 private void doSetRuntime(File baseDir) { 404 params.put(PARAM_RUNTIME, baseDir.toString()); 405 } 406 407 //Currently unused? 408 // 409 //public void setRuntime(RelativeFileSet fs) { 410 // runtime = fs; 411 //} 412 413 public RelativeFileSet getAppResource() { 414 return fetchParam(APP_RESOURCES); 415 } 416 417 public void setAppResource(RelativeFileSet fs) { 418 putUnlessNull(PARAM_APP_RESOURCES, fs); 419 } 420 421 public File getIcon() { 422 return fetchParam(ICON); 423 } 424 425 public void setIcon(File icon) { 426 putUnlessNull(PARAM_ICON, icon); 427 } 428 429 public String getApplicationCategory() { 430 return fetchParam(CATEGORY); 431 } 432 433 public void setApplicationCategory(String category) { 434 putUnlessNull(PARAM_CATEGORY, category); 435 } 436 437 public String getMainClassName() { 438 String applicationClass = getApplicationClass(); 439 440 if (applicationClass == null) { 441 return null; 442 } 443 444 int idx = applicationClass.lastIndexOf("."); 445 if (idx >= 0) { 446 return applicationClass.substring(idx+1); 447 } 448 return applicationClass; 449 } 450 451 public String getCopyright() { 452 return fetchParam(COPYRIGHT); 453 } 454 455 public void setCopyright(String c) { 456 putUnlessNull(PARAM_COPYRIGHT, c); 457 } 458 459 public String getIdentifier() { 460 return fetchParam(IDENTIFIER); 461 } 462 463 public void setIdentifier(String s) { 464 putUnlessNull(PARAM_IDENTIFIER, s); 465 } 466 467 private String mainJar = null; 468 private String mainJarClassPath = null; 469 private boolean useFXPackaging = true; 470 471 //are we packaging JavaFX application or regular executable Jar? 472 public boolean useJavaFXPackaging() { 473 if (mainJar == null) { 474 //this will find out answer 475 getMainApplicationJar(); 476 } 477 return useFXPackaging; 478 } 479 480 //For regular executable Jars we need to take care of classpath 481 //For JavaFX executable jars we do not need to pay attention to ClassPath entry in manifest 482 public String getAppClassPath() { 483 if (mainJar == null) { 484 //this will find out answer 485 getMainApplicationJar(); 486 } 487 if (useFXPackaging || mainJarClassPath == null) { 488 return ""; 489 } 490 return mainJarClassPath; 491 } 492 493 //assuming that application was packaged according to the rules 494 // we must have application jar, i.e. jar where we embed launcher 495 // and have main application class listed as main class! 496 //If there are more than one, or none - it will be treated as deployment error 497 // 498 //Note we look for both JavaFX executable jars and regular executable jars 499 //As long as main "application" entry point is the same it is main class 500 // (i.e. for FX jar we will use JavaFX manifest entry ...) 501 public String getMainApplicationJar() { 502 if (mainJar != null) { 503 return mainJar; 504 } 505 506 RelativeFileSet appResources = getAppResource(); 507 String applicationClass = getApplicationClass(); 508 509 if (appResources == null || applicationClass == null) { 510 return null; 511 } 512 File srcdir = appResources.getBaseDirectory(); 513 for (String fname : appResources.getIncludedFiles()) { 514 JarFile jf; 515 try { 516 jf = new JarFile(new File(srcdir, fname)); 517 Manifest m = jf.getManifest(); 518 Attributes attrs = (m != null) ? m.getMainAttributes() : null; 519 if (attrs != null) { 520 boolean javaMain = applicationClass.equals( 521 attrs.getValue(Attributes.Name.MAIN_CLASS)); 522 boolean fxMain = applicationClass.equals( 523 attrs.getValue(PackagerLib.MANIFEST_JAVAFX_MAIN)); 524 if (javaMain || fxMain) { 525 useFXPackaging = fxMain; 526 mainJar = fname; 527 mainJarClassPath = attrs.getValue(Attributes.Name.CLASS_PATH); 528 return mainJar; 529 } 530 } 531 } catch (IOException ignore) { 532 } 533 } 534 return null; 535 } 536 537 public String getVendor() { 538 return fetchParam(VENDOR); 539 } 540 541 public void setVendor(String vendor) { 542 putUnlessNull(PARAM_VENDOR, vendor); 543 } 544 545 public String getEmail() { 546 return fetchParam(String.class, PARAM_EMAIL); 547 } 548 549 public void setEmail(String email) { 550 putUnlessNull(PARAM_EMAIL, email); 551 } 552 553 public void putUnlessNull(String param, Object value) { 554 if (value != null) { 555 params.put(param, value); 556 } 557 } 558 559 }