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