1 /* 2 * Copyright (c) 2006, 2019, 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. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 package jnlp.converter.parser; 25 26 import java.net.URL; 27 import java.util.Properties; 28 import java.util.ArrayList; 29 import java.util.Arrays; 30 import java.util.Enumeration; 31 import java.util.LinkedList; 32 import java.util.List; 33 import java.util.concurrent.CopyOnWriteArrayList; 34 import jnlp.converter.HTTPHelper; 35 36 /** 37 * This class contains information about the codebase and properties, i.e., how 38 * to locate the classes and optional-packages 39 */ 40 public class ResourcesDesc implements ResourceType { 41 42 private final List<ResourceType> _list; 43 private volatile JNLPDesc _parent = null; 44 45 /** 46 * Create empty resource list 47 */ 48 public ResourcesDesc() { 49 _list = new CopyOnWriteArrayList<>(); 50 } 51 52 public JNLPDesc getParent() { 53 return _parent; 54 } 55 56 void setParent(JNLPDesc parent) { 57 _parent = parent; 58 for (int i = 0; i < _list.size(); i++) { 59 Object o = _list.get(i); 60 if (o instanceof JREDesc) { 61 JREDesc jredesc = (JREDesc) o; 62 if (jredesc.getNestedResources() != null) { 63 jredesc.getNestedResources().setParent(parent); 64 } 65 } 66 } 67 } 68 69 public void addResource(ResourceType rd) { 70 if (rd != null) { 71 _list.add(rd); 72 } 73 } 74 75 boolean isEmpty() { 76 return _list.isEmpty(); 77 } 78 79 public JARDesc[] getLocalJarDescs() { 80 ArrayList<JARDesc> jds = new ArrayList<>(_list.size()); 81 for (ResourceType rt : _list) { 82 if (rt instanceof JARDesc) { 83 jds.add((JARDesc) rt); 84 } 85 } 86 return jds.toArray(new JARDesc[jds.size()]); 87 } 88 89 public JREDesc getJreDesc() { 90 for (ResourceType rt : _list) { 91 if (rt instanceof JREDesc) { 92 return (JREDesc)rt; 93 } 94 } 95 96 return null; 97 } 98 99 public ExtensionDesc[] getExtensionDescs() throws Exception { 100 final ArrayList<ExtensionDesc> extList = new ArrayList<>(); 101 visit(new ResourceVisitor() { 102 @Override 103 public void visitExtensionDesc(ExtensionDesc ed) throws Exception { 104 // add all extensiondesc recursively 105 addExtToList(extList); 106 } 107 }); 108 return extList.toArray(new ExtensionDesc[extList.size()]); 109 } 110 111 public JARDesc[] getAllJarDescs() throws Exception { 112 List<JARDesc> jarList = new ArrayList<>(); 113 addJarsToList(jarList); 114 return jarList.toArray(new JARDesc[jarList.size()]); 115 } 116 117 /** 118 * Add to a list of all the ExtensionDesc. This method goes recusivly through 119 * all ExtensionDesc 120 */ 121 private void addExtToList(final List<ExtensionDesc> list) throws Exception { 122 // Iterate through list an add ext jnlp to the list. 123 visit(new ResourceVisitor() { 124 @Override 125 public void visitExtensionDesc(ExtensionDesc ed) throws Exception { 126 if (ed.getExtensionDesc() != null) { 127 ed.getExtensionDesc().getMainJar(); 128 ResourcesDesc rd = ed.getExtensionDesc().getResourcesDesc(); 129 if (rd != null) { 130 rd.addExtToList(list); 131 } 132 } 133 list.add(ed); 134 } 135 }); 136 } 137 138 private void addJarsToList(final List<JARDesc> list) throws Exception { 139 140 // Iterate through list an add resources to the list. 141 // The ordering of resources are preserved 142 visit(new ResourceVisitor() { 143 @Override 144 public void visitJARDesc(JARDesc jd) { 145 list.add(jd); 146 } 147 148 @Override 149 public void visitExtensionDesc(ExtensionDesc ed) throws Exception { 150 if (ed.getExtensionDesc() != null) { 151 ResourcesDesc rd = ed.getExtensionDesc().getResourcesDesc(); 152 if (rd != null) { 153 rd.addJarsToList(list); 154 } 155 } 156 } 157 }); 158 } 159 160 /** 161 * Get all the resources needed when a specific resource is requested. 162 * Returns null if no resource was found 163 */ 164 public JARDesc[] getResource(final URL location) throws Exception { 165 final JARDesc[] resources = new JARDesc[1]; 166 // Find the given resource 167 visit(new ResourceVisitor() { 168 @Override 169 public void visitJARDesc(JARDesc jd) { 170 if (GeneralUtil.sameURLs(jd.getLocation(), location)) { 171 resources[0] = jd; 172 } 173 } 174 }); 175 176 // Found no resource? 177 if (resources[0] == null) { 178 return null; 179 } 180 181 // No part, so just one resource 182 return resources; 183 } 184 185 /* Returns the Expected Main Jar 186 * first jar with attribute main="true" 187 * else first jar if none has that attribute 188 * will look in extensions, and nested resource blocks if matching 189 */ 190 protected JARDesc getMainJar() throws Exception { 191 // Normal trick to get around final arguments to inner classes 192 final JARDesc[] results = new JARDesc[2]; 193 194 visit(new ResourceVisitor() { 195 @Override 196 public void visitJARDesc(JARDesc jd) { 197 if (jd.isJavaFile()) { 198 // Keep track of first Java File 199 if (results[0] == null || results[0].isNativeLib()) { 200 results[0] = jd; 201 } 202 // Keep tack of Java File marked main 203 if (jd.isMainJarFile()) { 204 results[1] = jd; 205 } 206 } else if (jd.isNativeLib()) { 207 // if jnlp extension has only native lib 208 if (results[0] == null) { 209 results[0] = jd; 210 } 211 } 212 } 213 214 @Override 215 public void visitExtensionDesc(ExtensionDesc ed) throws Exception { 216 // only check if no main yet and it is not an installer 217 if (results[1] == null && !ed.isInstaller()) { 218 JNLPDesc extLd = ed.getExtensionDesc(); 219 if (extLd != null && extLd.isLibrary()) { 220 ResourcesDesc rd = extLd.getResourcesDesc(); 221 if (rd != null) { 222 // look for main jar in extension resource 223 rd.visit(this); 224 } 225 } 226 } 227 } 228 }); 229 230 // Default is the first, if none is specified as main. This might 231 // return NULL if there is no JAR resources. 232 JARDesc first = results[0]; 233 JARDesc main = results[1]; 234 235 // if main is null then return first; 236 // libraries have no such thing as a main jar, so return first; 237 // otherwise return main 238 // only returns null if there are no jars. 239 return (main == null) ? first : main; 240 } 241 242 /* 243 * Get the properties defined for this object 244 */ 245 public Properties getResourceProperties() throws Exception { 246 final Properties props = new Properties(); 247 visit(new ResourceVisitor() { 248 @Override 249 public void visitPropertyDesc(PropertyDesc pd) { 250 props.setProperty(pd.getKey(), pd.getValue()); 251 } 252 253 @Override 254 public void visitExtensionDesc(ExtensionDesc ed) throws Exception { 255 JNLPDesc jnlpd = ed.getExtensionDesc(); 256 ResourcesDesc rd = jnlpd.getResourcesDesc(); 257 if (rd != null) { 258 Properties extProps = rd.getResourceProperties(); 259 Enumeration e = extProps.propertyNames(); 260 while (e.hasMoreElements()) { 261 String key = (String) e.nextElement(); 262 String value = extProps.getProperty(key); 263 props.setProperty(key, value); 264 } 265 } 266 } 267 }); 268 return props; 269 } 270 271 /* 272 * Get the properties defined for this object, in the right order. 273 */ 274 public List<Property> getResourcePropertyList() throws Exception { 275 final LinkedList<Property> propList = new LinkedList<>(); 276 visit(new ResourceVisitor() { 277 @Override 278 public void visitPropertyDesc(PropertyDesc pd) { 279 propList.add(new Property(pd.getKey(), pd.getValue())); 280 } 281 }); 282 return propList; 283 } 284 285 /** 286 * visitor dispatch 287 */ 288 @Override 289 public void visit(ResourceVisitor rv) throws Exception { 290 for (int i = 0; i < _list.size(); i++) { 291 ResourceType rt = _list.get(i); 292 rt.visit(rv); 293 } 294 } 295 296 public void addNested(ResourcesDesc nested) throws Exception { 297 if (nested != null) { 298 nested.visit(new ResourceVisitor() { 299 @Override 300 public void visitJARDesc(JARDesc jd) { 301 _list.add(jd); 302 } 303 304 @Override 305 public void visitPropertyDesc(PropertyDesc pd) { 306 _list.add(pd); 307 } 308 309 @Override 310 public void visitExtensionDesc(ExtensionDesc ed) { 311 _list.add(ed); 312 } 313 }); 314 } 315 316 } 317 318 public static class JARDesc implements ResourceType { 319 320 private URL _location; 321 private String _locationString; 322 private String _version; 323 private boolean _isNativeLib; 324 private boolean _isMainFile; // Only used for Java JAR files (a main JAR file is implicitly eager) 325 private ResourcesDesc _parent; // Back-pointer to the Resources that contains this JAR 326 327 public JARDesc(URL location, String version, boolean isMainFile, boolean isNativeLib, ResourcesDesc parent) { 328 _location = location; 329 _locationString = GeneralUtil.toNormalizedString(location); 330 _version = version; 331 _isMainFile = isMainFile; 332 _isNativeLib = isNativeLib; 333 _parent = parent; 334 } 335 336 /** 337 * Type of JAR resource 338 */ 339 public boolean isNativeLib() { 340 return _isNativeLib; 341 } 342 343 public boolean isJavaFile() { 344 return !_isNativeLib; 345 } 346 347 /** 348 * Returns URL/version for JAR file 349 */ 350 public URL getVersionLocation() throws Exception { 351 if (getVersion() == null) { 352 return _location; 353 } else { 354 return GeneralUtil.getEmbeddedVersionURL(getLocation(), getVersion()); 355 } 356 } 357 358 public URL getLocation() { 359 return _location; 360 } 361 362 public String getVersion() { 363 return _version; 364 } 365 366 public String getName() { 367 // File can be separated by '/' or '\\' 368 int index; 369 int index1 = _locationString.lastIndexOf('/'); 370 int index2 = _locationString.lastIndexOf('\\'); 371 372 if (index1 >= index2) { 373 index = index1; 374 } else { 375 index = index2; 376 } 377 378 if (index != -1) { 379 return _locationString.substring(index + 1, _locationString.length()); 380 } 381 382 return null; 383 } 384 385 /** 386 * Returns if this is the main JAR file 387 */ 388 public boolean isMainJarFile() { 389 return _isMainFile; 390 } 391 392 /** 393 * Get parent LaunchDesc 394 */ 395 public ResourcesDesc getParent() { 396 return _parent; 397 } 398 399 /** 400 * Visitor dispatch 401 */ 402 public void visit(ResourceVisitor rv) { 403 rv.visitJARDesc(this); 404 } 405 } 406 407 public static class PropertyDesc implements ResourceType { 408 409 private String _key; 410 private String _value; 411 412 public PropertyDesc(String key, String value) { 413 _key = key; 414 _value = value; 415 } 416 417 // Accessors 418 public String getKey() { 419 return _key; 420 } 421 422 public String getValue() { 423 return _value; 424 } 425 426 /** 427 * Visitor dispatch 428 */ 429 public void visit(ResourceVisitor rv) { 430 rv.visitPropertyDesc(this); 431 } 432 433 } 434 435 public static class JREDesc implements ResourceType { 436 437 private String _version; 438 private long _maxHeap; 439 private long _minHeap; 440 private String _vmargs; 441 private ResourcesDesc _resourceDesc; 442 private JNLPDesc _extensioDesc; 443 private String _archList; 444 445 /* 446 * Constructor to create new instance based on the requirements from JNLP file. 447 */ 448 public JREDesc(String version, long minHeap, long maxHeap, String vmargs, 449 ResourcesDesc resourcesDesc, String archList) { 450 451 _version = version; 452 _maxHeap = maxHeap; 453 _minHeap = minHeap; 454 _vmargs = vmargs; 455 _resourceDesc = resourcesDesc; 456 _extensioDesc = null; 457 _archList = archList; 458 } 459 460 public String[] getArchList() { 461 return GeneralUtil.getStringList(_archList); 462 } 463 464 public String getVersion() { 465 return _version; 466 } 467 468 public long getMinHeap() { 469 return _minHeap; 470 } 471 472 public long getMaxHeap() { 473 return _maxHeap; 474 } 475 476 public String getVmArgs() { 477 return _vmargs; 478 } 479 480 public String[] getVmArgsList() { 481 return GeneralUtil.getStringList(_vmargs); 482 } 483 484 public ResourcesDesc getNestedResources() { 485 return _resourceDesc; 486 } 487 488 public JNLPDesc getExtensionDesc() { 489 return _extensioDesc; 490 } 491 492 public void setExtensionDesc(JNLPDesc ld) { 493 _extensioDesc = ld; 494 } 495 496 /* visitor dispatch */ 497 public void visit(ResourceVisitor rv) { 498 rv.visitJREDesc(this); 499 } 500 } 501 502 public static class Property implements Cloneable { 503 504 public static final String JNLP_VERSION_ENABLED = "jnlp.versionEnabled"; 505 506 String key; 507 String value; 508 509 public Property(String spec) { 510 spec = spec.trim(); 511 if (!spec.startsWith("-D") || spec.length() < 3) { 512 throw new IllegalArgumentException("Property invalid"); 513 } 514 515 int endKey = spec.indexOf("="); 516 if (endKey < 0) { 517 // it's legal to have no assignment 518 this.key = spec.substring(2); // skip "-D" 519 this.value = ""; 520 } else { 521 this.key = spec.substring(2, endKey); 522 this.value = spec.substring(endKey + 1); 523 } 524 } 525 526 public static Property createProperty(String spec) { 527 Property prop = null; 528 try { 529 prop = new Property(spec); 530 } catch (IllegalArgumentException iae) { 531 } 532 return prop; 533 } 534 535 public Property(String key, String value) { 536 this.key = key; 537 if (value != null) { 538 this.value = value; 539 } else { 540 this.value = ""; 541 } 542 } 543 544 public String getKey() { 545 return key; 546 } 547 548 public String getValue() { 549 return value; 550 } 551 552 // @return String representation, unquoted, unified presentation 553 public String toString() { 554 if (value.length() == 0) { 555 return "-D" + key; 556 } 557 return "-D" + key + "=" + value; 558 } 559 560 public void addTo(Properties props) { 561 props.setProperty(key, value); 562 } 563 564 // Hash Object 565 public boolean equals(Object o) { 566 if (!(o instanceof Property)) { 567 return false; 568 } 569 Property op = (Property) o; 570 int hashTheirs = op.hashCode(); 571 int hashThis = hashCode(); 572 return hashTheirs == hashThis; 573 } 574 575 public int hashCode() { 576 return key.hashCode(); 577 } 578 579 private static List<Object> jnlpProps = Arrays.asList(new Object[]{ 580 JNLP_VERSION_ENABLED 581 }); 582 583 public static boolean isJnlpProperty(String spec) { 584 try { 585 Property p = new Property(spec); 586 return isJnlpPropertyKey(p.getKey()); 587 } catch (Exception e) { 588 return false; 589 } 590 } 591 592 public static boolean isJnlpPropertyKey(String key) { 593 return key != null && jnlpProps.contains(key); 594 } 595 } 596 597 public static class ExtensionDesc implements ResourceType { 598 // Tag elements 599 600 private final URL _location; 601 private final String _locationString; 602 private final String _version; 603 private final URL _codebase; 604 605 // Link to launchDesc 606 private JNLPDesc _extensionLd; // Link to launchDesc for extension 607 608 public ExtensionDesc(URL location, String version) { 609 _location = location; 610 _locationString = GeneralUtil.toNormalizedString(location); 611 _version = version; 612 _codebase = GeneralUtil.asPathURL(GeneralUtil.getBase(location)); 613 _extensionLd = null; 614 } 615 616 public boolean isInstaller() throws Exception { 617 if (getExtensionDesc() != null) { 618 return _extensionLd.isInstaller(); 619 } 620 return false; 621 } 622 623 public URL getLocation() { 624 return _location; 625 } 626 627 public String getVersionLocation() throws Exception { 628 if (getVersion() == null) { 629 return _locationString; 630 } else { 631 return GeneralUtil.toNormalizedString(GeneralUtil.getEmbeddedVersionURL(getLocation(), getVersion())); 632 } 633 } 634 635 public String getVersion() { 636 return _version; 637 } 638 639 public URL getCodebase() { 640 return _codebase; 641 } 642 643 /* 644 * Information about the resources 645 */ 646 public JNLPDesc getExtensionDesc() throws Exception { 647 if (_extensionLd == null) { 648 byte[] bits = HTTPHelper.getJNLPBits(getVersionLocation(), _locationString); 649 _extensionLd = XMLFormat.parse(bits, getCodebase(), getVersionLocation()); 650 } 651 return _extensionLd; 652 } 653 654 public void setExtensionDesc(JNLPDesc desc) { 655 _extensionLd = desc; 656 } 657 658 /** 659 * Visitor dispatch 660 */ 661 public void visit(ResourceVisitor rv) throws Exception { 662 rv.visitExtensionDesc(this); 663 } 664 } 665 }