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 setAppModulePath(String appModulePath) { 184 putUnlessNull(APP_MODULE_PATH.getID(), appModulePath); 185 } 186 187 public void setLinkModulePath(String linkModulePath) { 188 putUnlessNull(LINK_MODULE_PATH.getID(), linkModulePath); 189 } 190 191 192 public String getApplicationID() { 193 return fetchParam(IDENTIFIER); 194 } 195 196 public String getPreferencesID() { 197 return fetchParam(PREFERENCES_ID); 198 } 199 200 public String getTitle() { 201 return fetchParam(TITLE); 202 } 203 204 public void setTitle(String title) { 205 putUnlessNull(PARAM_TITLE, title); 206 } 207 208 public String getApplicationClass() { 209 return fetchParam(MAIN_CLASS); 210 } 211 212 public void setApplicationClass(String applicationClass) { 213 putUnlessNull(PARAM_APPLICATION_CLASS, applicationClass); 214 } 215 216 public void setPrelaoderClass(String preloaderClass) { 217 putUnlessNull(PRELOADER_CLASS.getID(), preloaderClass); 218 } 219 220 public String getAppVersion() { 221 return fetchParam(VERSION); 222 } 223 224 public void setAppVersion(String version) { 225 putUnlessNull(PARAM_VERSION, version); 226 } 227 228 public String getDescription() { 229 return fetchParam(DESCRIPTION); 230 } 231 232 public void setDescription(String s) { 233 putUnlessNull(PARAM_DESCRIPTION, s); 234 } 235 236 public String getLicenseType() { 237 return fetchParam(LICENSE_TYPE); 238 } 239 240 public void setLicenseType(String version) { 241 putUnlessNull(PARAM_LICENSE_TYPE, version); 242 } 243 244 //path is relative to the application root 245 public void addLicenseFile(String path) { 246 List<String> licenseFiles = fetchParam(LICENSE_FILE); 247 if (licenseFiles == null || licenseFiles.isEmpty()) { 248 licenseFiles = new ArrayList<>(); 249 params.put(PARAM_LICENSE_FILE, licenseFiles); 250 } 251 licenseFiles.add(path); 252 } 253 254 public Boolean getSystemWide() { 255 return fetchParam(SYSTEM_WIDE); 256 } 257 258 public void setSystemWide(Boolean b) { 259 putUnlessNull(PARAM_SYSTEM_WIDE, b); 260 } 261 262 public void setServiceHint(Boolean b) { 263 putUnlessNull(PARAM_SERVICE_HINT, b); 264 } 265 266 public void setInstalldirChooser(Boolean b) { 267 putUnlessNull(PARAM_INSTALLDIR_CHOOSER, b); 268 } 269 270 public void setSignBundle(Boolean b) { putUnlessNull(SIGN_BUNDLE.getID(), b); } 271 272 public boolean isShortcutHint() { 273 return fetchParam(SHORTCUT_HINT); 274 } 275 276 public void setShortcutHint(Boolean v) { 277 putUnlessNull(PARAM_SHORTCUT, v); 278 } 279 280 public boolean isMenuHint() { 281 return fetchParam(MENU_HINT); 282 } 283 284 public void setMenuHint(Boolean v) { 285 putUnlessNull(PARAM_MENU, v); 286 } 287 288 public String getName() { 289 return fetchParam(APP_NAME); 290 } 291 292 public void setName(String name) { 293 putUnlessNull(PARAM_NAME, name); 294 } 295 296 @SuppressWarnings("deprecation") 297 public BundleType getType() { 298 return fetchParam(BundleType.class, PARAM_TYPE); 299 } 300 301 @SuppressWarnings("deprecation") 302 public void setType(BundleType type) { 303 putUnlessNull(PARAM_TYPE, type); 304 } 305 306 public String getBundleFormat() { 307 return fetchParam(String.class, PARAM_BUNDLE_FORMAT); 308 } 309 310 public void setBundleFormat(String t) { 311 putUnlessNull(PARAM_BUNDLE_FORMAT, t); 312 } 313 314 public boolean getVerbose() { 315 return fetchParam(VERBOSE); 316 } 317 318 public void setVerbose(Boolean verbose) { 319 putUnlessNull(VERBOSE.getID(), verbose); 320 } 321 322 public List<String> getLicenseFile() { 323 return fetchParam(LICENSE_FILE); 324 } 325 326 public List<String> getJvmargs() { 327 return JVM_OPTIONS.fetchFrom(params); 328 } 329 330 public List<String> getArguments() { 331 return ARGUMENTS.fetchFrom(params); 332 } 333 334 //Validation approach: 335 // - JRE marker (rt.jar) 336 // - FX marker (jfxrt.jar) 337 // - JDK marker (tools.jar) 338 private static boolean checkJDKRoot(File jdkRoot) { 339 File rtJar = new File(jdkRoot, "jre/lib/rt.jar"); 340 if (!rtJar.exists()) { 341 Log.verbose("rt.jar is not found at " + rtJar.getAbsolutePath()); 342 return false; 343 } 344 345 File jfxJar = new File(jdkRoot, "jre/lib/ext/jfxrt.jar"); 346 if (!jfxJar.exists()) { 347 //Try again with new location 348 jfxJar = new File(jdkRoot, "jre/lib/jfxrt.jar"); 349 if (!jfxJar.exists()) { 350 Log.verbose("jfxrt.jar is not found at " + jfxJar.getAbsolutePath()); 351 return false; 352 } 353 } 354 355 356 File toolsJar = new File(jdkRoot, "lib/tools.jar"); 357 if (!toolsJar.exists()) { 358 Log.verbose("tools.jar is not found at " + toolsJar.getAbsolutePath()); 359 return false; 360 } 361 362 return true; 363 } 364 365 //Depending on platform and user input we may get different "references" 366 //Should support 367 // - java.home 368 // - reference to JDK install folder 369 // - should NOT support JRE dir 370 //Note: input could be null (then we asked to use system JRE) 371 // or it must be valid directory 372 //Returns null on validation failure. Returns jre root if ok. 373 public static File validateRuntimeLocation(File javaHome) { 374 if (javaHome == null) { 375 return null; 376 } 377 File jdkRoot; 378 379 boolean isMac = System.getProperty("os.name").toLowerCase().contains("os x"); 380 381 File rtJar = new File(javaHome, "lib/rt.jar"); 382 if (rtJar.exists()) { //must be "java.home" case 383 //i.e. we are in JRE folder 384 jdkRoot = javaHome.getParentFile(); 385 } else { //expect it to be root of JDK installation folder 386 //On Mac it could be jdk/ or jdk/Contents/Home 387 //Norm to jdk/Contents/Home for validation 388 if (isMac) { 389 File f = new File(javaHome, "Contents/Home"); 390 if (f.exists() && f.isDirectory()) { 391 javaHome = f; 392 } 393 } 394 jdkRoot = javaHome; 395 } 396 397 if (!checkJDKRoot(jdkRoot)) { 398 throw new RuntimeException( 399 "Can not find JDK artifacts in specified location: " 400 + javaHome.getAbsolutePath()); 401 } 402 403 return new File(jdkRoot, "jre"); 404 } 405 406 //select subset of given runtime using predefined rules 407 public void setRuntime(File baseDir) { 408 baseDir = validateRuntimeLocation(baseDir); 409 410 //mistake or explicit intent to use system runtime 411 if (baseDir == null) { 412 Log.verbose("No Java runtime to embed. Package will need system Java."); 413 params.put(PARAM_RUNTIME, null); 414 return; 415 } 416 doSetRuntime(baseDir); 417 } 418 419 //input dir "jdk/jre" (i.e. jre folder in the jdk) 420 private void doSetRuntime(File baseDir) { 421 params.put(PARAM_RUNTIME, baseDir.toString()); 422 } 423 424 //Currently unused? 425 // 426 //public void setRuntime(RelativeFileSet fs) { 427 // runtime = fs; 428 //} 429 430 public com.oracle.tools.packager.RelativeFileSet getAppResource() { 431 return fetchParam(APP_RESOURCES); 432 } 433 434 public void setAppResource(com.oracle.tools.packager.RelativeFileSet fs) { 435 putUnlessNull(PARAM_APP_RESOURCES, fs); 436 } 437 438 public void setAppResourcesList(List<com.oracle.tools.packager.RelativeFileSet> rfs) { 439 putUnlessNull(APP_RESOURCES_LIST.getID(), rfs); 440 } 441 442 public File getIcon() { 443 return fetchParam(ICON); 444 } 445 446 public void setIcon(File icon) { 447 putUnlessNull(PARAM_ICON, icon); 448 } 449 450 public String getApplicationCategory() { 451 return fetchParam(CATEGORY); 452 } 453 454 public void setApplicationCategory(String category) { 455 putUnlessNull(PARAM_CATEGORY, category); 456 } 457 458 public String getMainClassName() { 459 String applicationClass = getApplicationClass(); 460 461 if (applicationClass == null) { 462 return null; 463 } 464 465 int idx = applicationClass.lastIndexOf("."); 466 if (idx >= 0) { 467 return applicationClass.substring(idx+1); 468 } 469 return applicationClass; 470 } 471 472 public String getCopyright() { 473 return fetchParam(COPYRIGHT); 474 } 475 476 public void setCopyright(String c) { 477 putUnlessNull(PARAM_COPYRIGHT, c); 478 } 479 480 public String getIdentifier() { 481 return fetchParam(IDENTIFIER); 482 } 483 484 public void setIdentifier(String s) { 485 putUnlessNull(PARAM_IDENTIFIER, s); 486 } 487 488 private String mainJar = null; 489 private String mainJarClassPath = null; 490 private boolean useFXPackaging = true; 491 492 //are we packaging JavaFX application or regular executable Jar? 493 public boolean useJavaFXPackaging() { 494 if (mainJar == null) { 495 //this will find out answer 496 getMainApplicationJar(); 497 } 498 return useFXPackaging; 499 } 500 501 //For regular executable Jars we need to take care of classpath 502 //For JavaFX executable jars we do not need to pay attention to ClassPath entry in manifest 503 public String getAppClassPath() { 504 if (mainJar == null) { 505 //this will find out answer 506 getMainApplicationJar(); 507 } 508 if (useFXPackaging || mainJarClassPath == null) { 509 return ""; 510 } 511 return mainJarClassPath; 512 } 513 514 //assuming that application was packaged according to the rules 515 // we must have application jar, i.e. jar where we embed launcher 516 // and have main application class listed as main class! 517 //If there are more than one, or none - it will be treated as deployment error 518 // 519 //Note we look for both JavaFX executable jars and regular executable jars 520 //As long as main "application" entry point is the same it is main class 521 // (i.e. for FX jar we will use JavaFX manifest entry ...) 522 public String getMainApplicationJar() { 523 if (mainJar != null) { 524 return mainJar; 525 } 526 527 com.oracle.tools.packager.RelativeFileSet appResources = getAppResource(); 528 String applicationClass = getApplicationClass(); 529 530 if (appResources == null || applicationClass == null) { 531 return null; 532 } 533 File srcdir = appResources.getBaseDirectory(); 534 for (String fname : appResources.getIncludedFiles()) { 535 JarFile jf; 536 try { 537 jf = new JarFile(new File(srcdir, fname)); 538 Manifest m = jf.getManifest(); 539 Attributes attrs = (m != null) ? m.getMainAttributes() : null; 540 if (attrs != null) { 541 boolean javaMain = applicationClass.equals( 542 attrs.getValue(Attributes.Name.MAIN_CLASS)); 543 boolean fxMain = applicationClass.equals( 544 attrs.getValue(MANIFEST_JAVAFX_MAIN)); 545 if (javaMain || fxMain) { 546 useFXPackaging = fxMain; 547 mainJar = fname; 548 mainJarClassPath = attrs.getValue(Attributes.Name.CLASS_PATH); 549 return mainJar; 550 } 551 } 552 } catch (IOException ignore) { 553 } 554 } 555 return null; 556 } 557 558 public String getVendor() { 559 return fetchParam(VENDOR); 560 } 561 562 public void setVendor(String vendor) { 563 putUnlessNull(PARAM_VENDOR, vendor); 564 } 565 566 public String getEmail() { 567 return fetchParam(String.class, PARAM_EMAIL); 568 } 569 570 public void setEmail(String email) { 571 putUnlessNull(PARAM_EMAIL, email); 572 } 573 574 public void putUnlessNull(String param, Object value) { 575 if (value != null) { 576 params.put(param, value); 577 } 578 } 579 580 public void putUnlessNullOrEmpty(String param, Collection value) { 581 if (value != null && !value.isEmpty()) { 582 params.put(param, value); 583 } 584 } 585 586 public void putUnlessNullOrEmpty(String param, Map value) { 587 if (value != null && !value.isEmpty()) { 588 params.put(param, value); 589 } 590 } 591 592 }