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