|
1 /* |
|
2 * Copyright (c) 2006, 2018, 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 } |