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.Arrays; 28 import java.util.ArrayList; 29 import jnlp.converter.JNLPConverter; 30 import jnlp.converter.parser.exception.MissingFieldException; 31 import jnlp.converter.parser.exception.BadFieldException; 32 import jnlp.converter.parser.exception.JNLParseException; 33 import jnlp.converter.parser.xml.XMLEncoding; 34 import jnlp.converter.parser.xml.XMLParser; 35 import jnlp.converter.parser.xml.XMLNode; 36 import jnlp.converter.parser.JNLPDesc.AssociationDesc; 37 import jnlp.converter.parser.JNLPDesc.IconDesc; 38 import jnlp.converter.parser.JNLPDesc.InformationDesc; 39 import jnlp.converter.parser.JNLPDesc.ShortcutDesc; 40 import jnlp.converter.parser.ResourcesDesc.JARDesc; 41 import jnlp.converter.parser.ResourcesDesc.JREDesc; 42 import jnlp.converter.Log; 43 import jnlp.converter.parser.ResourcesDesc.ExtensionDesc; 44 import jnlp.converter.parser.ResourcesDesc.PropertyDesc; 45 import org.xml.sax.SAXParseException; 46 47 public class XMLFormat { 48 49 public static XMLNode parseBits(byte[] bits) throws JNLParseException { 50 return parse(decode(bits)); 51 } 52 53 private static String decode(byte[] bits) throws JNLParseException { 54 try { 55 return XMLEncoding.decodeXML(bits); 56 } catch (Exception e) { 57 throw new JNLParseException(e, 58 "exception determining encoding of jnlp file", 0); 59 } 60 } 61 62 private static XMLNode parse(String source) throws JNLParseException { 63 try { 64 return (new XMLParser(source).parse()); 65 } catch (SAXParseException spe) { 66 throw new JNLParseException(spe, 67 "exception parsing jnlp file", spe.getLineNumber()); 68 } catch (Exception e) { 69 throw new JNLParseException(e, 70 "exception parsing jnlp file", 0); 71 } 72 } 73 74 /** 75 * thisCodebase, if set, is used to determine the codebase, 76 * if JNLP codebase is not absolute. 77 * 78 * @param thisCodebase base URL of this JNLPDesc location 79 */ 80 public static JNLPDesc parse(byte[] bits, URL thisCodebase, String jnlp) 81 throws Exception { 82 83 JNLPDesc jnlpd = new JNLPDesc(); 84 String source = decode(bits).trim(); 85 XMLNode root = parse(source); 86 87 if (root == null || root.getName() == null) { 88 throw new JNLParseException(null, null, 0); 89 } 90 91 // Check that root element is a <jnlp> tag 92 if (!root.getName().equals("jnlp")) { 93 throw (new MissingFieldException(source, "<jnlp>")); 94 } 95 96 // Read <jnlp> attributes (path is empty, i.e., "") 97 // (spec, version, codebase, href) 98 String specVersion = XMLUtils.getAttribute(root, "", "spec", "1.0+"); 99 jnlpd.setSpecVersion(specVersion); 100 String version = XMLUtils.getAttribute(root, "", "version"); 101 jnlpd.setVersion(version); 102 103 // Make sure the codebase URL ends with a '/'. 104 // 105 // Regarding the JNLP spec, 106 // the thisCodebase is used to determine the codebase. 107 // codebase = new URL(thisCodebase, codebase) 108 URL codebase = GeneralUtil.asPathURL(XMLUtils.getAttributeURL(source, 109 thisCodebase, root, "", "codebase")); 110 if (codebase == null && thisCodebase != null) { 111 codebase = thisCodebase; 112 } 113 jnlpd.setCodebase(codebase.toExternalForm()); 114 115 // Get href for JNLP file 116 URL href = XMLUtils.getAttributeURL(source, codebase, root, "", "href"); 117 jnlpd.setHref(href.toExternalForm()); 118 119 // Read <security> attributes 120 if (XMLUtils.isElementPath(root, "<security><all-permissions>")) { 121 jnlpd.setIsSandbox(false); 122 } else if (XMLUtils.isElementPath(root, 123 "<security><j2ee-application-client-permissions>")) { 124 jnlpd.setIsSandbox(false); 125 } 126 127 // We can be fxapp, and also be applet, or application, or neither 128 boolean isFXApp = false; 129 boolean isApplet = false; 130 if (XMLUtils.isElementPath(root, "<javafx-desc>")) { 131 // no new type for javafx-desc - needs one of the others 132 buildFXAppDesc(source, root, "<javafx-desc>", jnlpd); 133 jnlpd.setIsFXApp(true); 134 isFXApp = true; 135 } 136 137 /* 138 * Note - the jnlp specification says there must be exactly one of 139 * the descriptor types. This code has always violated (or at least 140 * not checked for) that condition. 141 * Instead it uses precedent order app, component, installer, applet 142 * and ignores any other descriptors given. 143 */ 144 if (XMLUtils.isElementPath(root, "<application-desc>")) { 145 buildApplicationDesc(source, root, jnlpd); 146 } else if (XMLUtils.isElementPath(root, "<component-desc>")) { 147 jnlpd.setIsLibrary(true); 148 } else if (XMLUtils.isElementPath(root, "<installer-desc>")) { 149 Log.warning("<installer-desc> is not supported and will be ignored in " + jnlp); 150 jnlpd.setIsInstaller(true); 151 } else if (XMLUtils.isElementPath(root, "<applet-desc>")) { 152 isApplet = true; 153 } else { 154 if (!isFXApp) { 155 throw (new MissingFieldException(source, 156 "<jnlp>(<application-desc>|<applet-desc>|" + 157 "<installer-desc>|<component-desc>)")); 158 } 159 } 160 161 if (isApplet && !isFXApp) { 162 Log.error("Applet based applications deployed with <applet-desc> element are not supported."); 163 } 164 165 if (!jnlpd.isLibrary() && !jnlpd.isInstaller()) { 166 buildInformationDesc(source, codebase, root, jnlpd); 167 } 168 169 if (!jnlpd.isInstaller()) { 170 buildResourcesDesc(source, codebase, root, false, jnlpd); 171 } 172 173 if (!jnlpd.isLibrary() && !jnlpd.isInstaller()) { 174 jnlpd.parseResourceDesc(); 175 } 176 177 if (!jnlpd.isInstaller()) { 178 if (jnlpd.isSandbox()) { 179 if (jnlpd.isLibrary()) { 180 Log.warning(jnlp + " is sandbox extension. JNLPConverter does not support sandbox environment and converted application will run without security manager."); 181 } else { 182 Log.warning("This is sandbox Web-Start application. JNLPConverter does not support sandbox environment and converted application will run without security manager."); 183 } 184 } 185 } 186 187 return jnlpd; 188 } 189 190 /** 191 * Create a combine informationDesc in the two informationDesc. 192 * The information present in id1 overwrite the information present in id2 193 */ 194 private static InformationDesc combineInformationDesc( 195 InformationDesc id1, InformationDesc id2) { 196 if (id1 == null) { 197 return id2; 198 } 199 if (id2 == null) { 200 return id1; 201 } 202 203 String t1 = id1.getTitle(); 204 String title = (t1 != null && t1.length() > 0) ? 205 t1 : id2.getTitle(); 206 String v1 = id1.getVendor(); 207 String vendor = (v1 != null && v1.length() > 0) ? 208 v1 : id2.getVendor(); 209 210 /** Copy descriptions */ 211 String[] descriptions = new String[InformationDesc.NOF_DESC]; 212 for (int i = 0; i < descriptions.length; i++) { 213 descriptions[i] = (id1.getDescription(i) != null) 214 ? id1.getDescription(i) : id2.getDescription(i); 215 } 216 217 /** Icons */ 218 ArrayList<IconDesc> iconList = new ArrayList<>(); 219 if (id2.getIcons() != null) { 220 iconList.addAll(Arrays.asList(id2.getIcons())); 221 } 222 if (id1.getIcons() != null) { 223 iconList.addAll(Arrays.asList(id1.getIcons())); 224 } 225 IconDesc[] icons = new IconDesc[iconList.size()]; 226 icons = iconList.toArray(icons); 227 228 ShortcutDesc hints = (id1.getShortcut() != null) ? 229 id1.getShortcut() : id2.getShortcut(); 230 231 AssociationDesc[] asd = ( AssociationDesc[] ) addArrays( 232 (Object[])id1.getAssociations(), (Object[])id2.getAssociations()); 233 234 return new InformationDesc(title, 235 vendor, 236 descriptions, 237 icons, 238 hints, 239 asd); 240 } 241 242 /** Extract data from <information> tag */ 243 private static void buildInformationDesc(final String source, final URL codebase, XMLNode root, JNLPDesc jnlpd) 244 throws MissingFieldException, BadFieldException { 245 final ArrayList<InformationDesc> list = new ArrayList<>(); 246 247 // Iterates over all <information> nodes ignoring the type 248 XMLUtils.visitElements(root, 249 "<information>", new XMLUtils.ElementVisitor() { 250 @Override 251 public void visitElement(XMLNode e) throws 252 BadFieldException, MissingFieldException { 253 254 // Check for right os, arch, and locale 255 String[] os = GeneralUtil.getStringList( 256 XMLUtils.getAttribute(e, "", "os", null)); 257 String[] arch = GeneralUtil.getStringList( 258 XMLUtils.getAttribute(e, "", "arch", null)); 259 String[] locale = GeneralUtil.getStringList( 260 XMLUtils.getAttribute(e, "", "locale", null)); 261 if (GeneralUtil.prefixMatchStringList( 262 os, GeneralUtil.getOSFullName()) && 263 GeneralUtil.prefixMatchArch(arch) && 264 matchDefaultLocale(locale)) 265 { 266 // Title, vendor 267 String title = XMLUtils.getElementContents(e, "<title>"); 268 String vendor = XMLUtils.getElementContents(e, "<vendor>"); 269 270 // Descriptions 271 String[] descriptions = 272 new String[InformationDesc.NOF_DESC]; 273 descriptions[InformationDesc.DESC_DEFAULT] = 274 XMLUtils.getElementContentsWithAttribute( 275 e, "<description>", "kind", "", null); 276 descriptions[InformationDesc.DESC_ONELINE] = 277 XMLUtils.getElementContentsWithAttribute( 278 e, "<description>", "kind", "one-line", null); 279 descriptions[InformationDesc.DESC_SHORT] = 280 XMLUtils.getElementContentsWithAttribute( 281 e, "<description>", "kind", "short", null); 282 descriptions[InformationDesc.DESC_TOOLTIP] = 283 XMLUtils.getElementContentsWithAttribute( 284 e, "<description>", "kind", "tooltip", null); 285 286 // Icons 287 IconDesc[] icons = getIconDescs(source, codebase, e); 288 289 // Shortcut hints 290 ShortcutDesc shortcuts = getShortcutDesc(e); 291 292 // Association hints 293 AssociationDesc[] associations = getAssociationDesc( 294 source, codebase, e); 295 296 list.add(new InformationDesc( 297 title, vendor, descriptions, icons, 298 shortcuts, associations)); 299 } 300 } 301 }); 302 303 /* Combine all information desc. information in a single one for 304 * the current locale using the following priorities: 305 * 1. locale == language_country_variant 306 * 2. locale == lauguage_country 307 * 3. locale == lauguage 308 * 4. no or empty locale 309 */ 310 InformationDesc normId = new InformationDesc(null, null, null, null, null, null); 311 for (InformationDesc id : list) { 312 normId = combineInformationDesc(id, normId); 313 } 314 315 jnlpd.setTitle(normId.getTitle()); 316 jnlpd.setVendor(normId.getVendor()); 317 jnlpd.setDescriptions(normId.getDescription()); 318 jnlpd.setIcons(normId.getIcons()); 319 jnlpd.setShortcuts(normId.getShortcut()); 320 jnlpd.setAssociations(normId.getAssociations()); 321 } 322 323 private static Object[] addArrays (Object[] a1, Object[] a2) { 324 if (a1 == null) { 325 return a2; 326 } 327 if (a2 == null) { 328 return a1; 329 } 330 ArrayList<Object> list = new ArrayList<>(); 331 int i; 332 for (i=0; i<a1.length; list.add(a1[i++])); 333 for (i=0; i<a2.length; list.add(a2[i++])); 334 return list.toArray(a1); 335 } 336 337 public static boolean matchDefaultLocale(String[] localeStr) { 338 return GeneralUtil.matchLocale(localeStr, GeneralUtil.getDefaultLocale()); 339 } 340 341 /** Extract data from <resources> tag. There is only one. */ 342 static void buildResourcesDesc(final String source, 343 final URL codebase, XMLNode root, final boolean ignoreJres, JNLPDesc jnlpd) 344 throws MissingFieldException, BadFieldException { 345 // Extract classpath directives 346 final ResourcesDesc rdesc = new ResourcesDesc(); 347 348 // Iterate over all entries 349 XMLUtils.visitElements(root, "<resources>", 350 new XMLUtils.ElementVisitor() { 351 @Override 352 public void visitElement(XMLNode e) 353 throws MissingFieldException, BadFieldException { 354 // Check for right os, archictecture, and locale 355 String[] os = GeneralUtil.getStringList( 356 XMLUtils.getAttribute(e, "", "os", null)); 357 final String arch = XMLUtils.getAttribute(e, "", "arch", null); 358 String[] locale = GeneralUtil.getStringList( 359 XMLUtils.getAttribute(e, "", "locale", null)); 360 if (GeneralUtil.prefixMatchStringList( 361 os, GeneralUtil.getOSFullName()) 362 && matchDefaultLocale(locale)) { 363 // Now visit all children in this node 364 XMLUtils.visitChildrenElements(e, 365 new XMLUtils.ElementVisitor() { 366 @Override 367 public void visitElement(XMLNode e2) 368 throws MissingFieldException, BadFieldException { 369 handleResourceElement(source, codebase, 370 e2, rdesc, ignoreJres, arch, jnlpd); 371 } 372 }); 373 } 374 } 375 }); 376 377 if (!rdesc.isEmpty()) { 378 jnlpd.setResourcesDesc(rdesc); 379 } 380 } 381 382 private static IconDesc[] getIconDescs(final String source, 383 final URL codebase, XMLNode e) 384 throws MissingFieldException, BadFieldException { 385 final ArrayList<IconDesc> answer = new ArrayList<>(); 386 XMLUtils.visitElements(e, "<icon>", new XMLUtils.ElementVisitor() { 387 @Override 388 public void visitElement(XMLNode icon) throws 389 MissingFieldException, BadFieldException { 390 String kindStr = XMLUtils.getAttribute(icon, "", "kind", ""); 391 URL href = XMLUtils.getRequiredURL(source, codebase, icon, "", "href"); 392 393 if (href != null) { 394 if (!JNLPConverter.isIconSupported(href.toExternalForm())) { 395 return; 396 } 397 } 398 399 int kind; 400 if (kindStr == null || kindStr.isEmpty() || kindStr.equals("default")) { 401 kind = IconDesc.ICON_KIND_DEFAULT; 402 } else if (kindStr.equals("shortcut")) { 403 kind = IconDesc.ICON_KIND_SHORTCUT; 404 } else { 405 Log.warning("Ignoring unsupported icon \"" + href + "\" with kind \"" + kindStr + "\"."); 406 return; 407 } 408 409 answer.add(new IconDesc(href, kind)); 410 } 411 }); 412 return answer.toArray(new IconDesc[answer.size()]); 413 } 414 415 private static ShortcutDesc getShortcutDesc(XMLNode e) 416 throws MissingFieldException, BadFieldException { 417 final ArrayList<ShortcutDesc> shortcuts = new ArrayList<>(); 418 419 XMLUtils.visitElements(e, "<shortcut>", new XMLUtils.ElementVisitor() { 420 @Override 421 public void visitElement(XMLNode shortcutNode) 422 throws MissingFieldException, BadFieldException { 423 boolean desktopHinted = 424 XMLUtils.isElementPath(shortcutNode, "<desktop>"); 425 boolean menuHinted = 426 XMLUtils.isElementPath(shortcutNode, "<menu>"); 427 String submenuHinted = 428 XMLUtils.getAttribute(shortcutNode, "<menu>", "submenu"); 429 shortcuts.add(new ShortcutDesc(desktopHinted, menuHinted, submenuHinted)); 430 } 431 }); 432 433 if (shortcuts.size() > 0) { 434 return shortcuts.get(0); 435 } 436 return null; 437 } 438 439 private static AssociationDesc[] getAssociationDesc(final String source, 440 final URL codebase, XMLNode e) 441 throws MissingFieldException, BadFieldException { 442 final ArrayList<AssociationDesc> answer = new ArrayList<>(); 443 XMLUtils.visitElements(e, "<association>", 444 new XMLUtils.ElementVisitor() { 445 @Override 446 public void visitElement(XMLNode node) 447 throws MissingFieldException, BadFieldException { 448 449 String extensions = XMLUtils.getAttribute( 450 node, "", "extensions"); 451 452 String mimeType = XMLUtils.getAttribute( 453 node, "", "mime-type"); 454 String description = XMLUtils.getElementContents( 455 node, "<description>"); 456 457 URL icon = XMLUtils.getAttributeURL( 458 source, codebase, node, "<icon>", "href"); 459 460 if (!JNLPConverter.isIconSupported(icon.toExternalForm())) { 461 icon = null; 462 } 463 464 if (extensions == null && mimeType == null) { 465 throw new MissingFieldException(source, 466 "<association>(<extensions><mime-type>)"); 467 } else if (extensions == null) { 468 throw new MissingFieldException(source, 469 "<association><extensions>"); 470 } else if (mimeType == null) { 471 throw new MissingFieldException(source, 472 "<association><mime-type>"); 473 } 474 475 // don't support uppercase extension and mime-type on gnome. 476 if ("gnome".equals(System.getProperty("sun.desktop"))) { 477 extensions = extensions.toLowerCase(); 478 mimeType = mimeType.toLowerCase(); 479 } 480 481 answer.add(new AssociationDesc(extensions, mimeType, 482 description, icon)); 483 } 484 }); 485 return answer.toArray( 486 new AssociationDesc[answer.size()]); 487 } 488 489 /** Handle the individual entries in a resource desc */ 490 private static void handleResourceElement(String source, URL codebase, 491 XMLNode e, ResourcesDesc rdesc, boolean ignoreJres, String arch, JNLPDesc jnlpd) 492 throws MissingFieldException, BadFieldException { 493 494 String tag = e.getName(); 495 496 boolean matchArch = GeneralUtil.prefixMatchArch( 497 GeneralUtil.getStringList(arch)); 498 499 500 if (matchArch && (tag.equals("jar") || tag.equals("nativelib"))) { 501 /* 502 * jar/nativelib elements 503 */ 504 URL href = XMLUtils.getRequiredURL(source, codebase, e, "", "href"); 505 String version = XMLUtils.getAttribute(e, "", "version", null); 506 507 String mainStr = XMLUtils.getAttribute(e, "", "main"); 508 boolean isNativeLib = tag.equals("nativelib"); 509 510 boolean isMain = "true".equalsIgnoreCase(mainStr); 511 512 JARDesc jd = new JARDesc(href, version, isMain, isNativeLib, rdesc); 513 rdesc.addResource(jd); 514 } else if (matchArch && tag.equals("property")) { 515 /* 516 * property tag 517 */ 518 String name = XMLUtils.getRequiredAttribute(source, e, "", "name"); 519 String value = XMLUtils.getRequiredAttributeEmptyOK( 520 source, e, "", "value"); 521 522 rdesc.addResource(new PropertyDesc(name, value)); 523 } else if (matchArch && tag.equals("extension")) { 524 URL href = XMLUtils.getRequiredURL(source, codebase, e, "", "href"); 525 String version = XMLUtils.getAttribute(e, "", "version", null); 526 rdesc.addResource(new ExtensionDesc(href, version)); 527 } else if ((tag.equals("java") || tag.equals("j2se")) && !ignoreJres) { 528 /* 529 * j2se element 530 */ 531 String version = 532 XMLUtils.getRequiredAttribute(source, e, "", "version"); 533 String minheapstr = 534 XMLUtils.getAttribute(e, "", "initial-heap-size"); 535 String maxheapstr = 536 XMLUtils.getAttribute(e, "", "max-heap-size"); 537 538 String vmargs = 539 XMLUtils.getAttribute(e, "", "java-vm-args"); 540 541 if (jnlpd.isJRESet()) { 542 if (vmargs == null) { 543 vmargs = "none"; 544 } 545 Log.warning("Ignoring repeated element <" + tag + "> with version " + version + 546 " and java-vm-args: " + vmargs); 547 return; 548 } 549 550 long minheap = GeneralUtil.heapValToLong(minheapstr); 551 long maxheap = GeneralUtil.heapValToLong(maxheapstr); 552 553 ResourcesDesc cbs = null; 554 buildResourcesDesc(source, codebase, e, true, null); 555 556 // JRE 557 JREDesc jreDesc = new JREDesc( 558 version, 559 minheap, 560 maxheap, 561 vmargs, 562 cbs, 563 arch); 564 565 rdesc.addResource(jreDesc); 566 567 jnlpd.setIsJRESet(true); 568 } 569 } 570 571 /** Extract data from the application-desc tag */ 572 private static void buildApplicationDesc(final String source, 573 XMLNode root, JNLPDesc jnlpd) throws MissingFieldException, BadFieldException { 574 575 String mainclass = XMLUtils.getClassName(source, root, 576 "<application-desc>", "main-class", false); 577 String appType = XMLUtils.getAttribute(root, "<application-desc>", 578 "type", "Java"); 579 String progressclass = XMLUtils.getClassName(source, root, 580 "<application-desc>", "progress-class", false); 581 if (progressclass != null && !progressclass.isEmpty()) { 582 Log.warning("JNLPConverter does not support progress indication. \"" + progressclass + "\" will not be loaded and will be ignored."); 583 } 584 585 if (!("Java".equalsIgnoreCase(appType) || 586 "JavaFx".equalsIgnoreCase(appType))) { 587 throw new BadFieldException(source, XMLUtils.getPathString(root) + 588 "<application-desc>type", appType); 589 } 590 591 if ("JavaFx".equalsIgnoreCase(appType)) { 592 jnlpd.setIsFXApp(true); 593 } 594 595 XMLUtils.visitElements(root, "<application-desc><argument>", new XMLUtils.ElementVisitor() { 596 @Override 597 public void visitElement(XMLNode e) throws MissingFieldException, BadFieldException { 598 String arg = XMLUtils.getElementContents(e, "", null); 599 if (arg == null) { 600 throw new BadFieldException(source, XMLUtils.getPathString(e), ""); 601 } 602 jnlpd.addArguments(arg); 603 } 604 }); 605 606 XMLUtils.visitElements(root, "<application-desc><param>", 607 new XMLUtils.ElementVisitor() { 608 @Override 609 public void visitElement(XMLNode e) throws MissingFieldException, 610 BadFieldException { 611 String pn = XMLUtils.getRequiredAttribute( 612 source, e, "", "name"); 613 String pv = XMLUtils.getRequiredAttributeEmptyOK( 614 source, e, "", "value"); 615 jnlpd.setProperty(pn, pv); 616 } 617 }); 618 jnlpd.setMainClass(mainclass, false); 619 } 620 621 /** Extract data from the javafx-desc tag */ 622 private static void buildFXAppDesc(final String source, 623 XMLNode root, String element, JNLPDesc jnlpd) 624 throws MissingFieldException, BadFieldException { 625 String mainclass = XMLUtils.getClassName(source, root, element, 626 "main-class", true); 627 String name = XMLUtils.getRequiredAttribute(source, root, 628 "<javafx-desc>", "name"); 629 630 /* extract arguments */ 631 XMLUtils.visitElements(root, "<javafx-desc><argument>", new XMLUtils.ElementVisitor() { 632 @Override 633 public void visitElement(XMLNode e) throws MissingFieldException, BadFieldException { 634 String arg = XMLUtils.getElementContents(e, "", null); 635 if (arg == null) { 636 throw new BadFieldException(source, XMLUtils.getPathString(e), ""); 637 } 638 jnlpd.addArguments(arg); 639 } 640 }); 641 642 /* extract parameters */ 643 XMLUtils.visitElements(root, "<javafx-desc><param>", 644 new XMLUtils.ElementVisitor() { 645 @Override 646 public void visitElement(XMLNode e) throws MissingFieldException, 647 BadFieldException { 648 String pn = XMLUtils.getRequiredAttribute( 649 source, e, "", "name"); 650 String pv = XMLUtils.getRequiredAttributeEmptyOK( 651 source, e, "", "value"); 652 jnlpd.setProperty(pn, pv); 653 } 654 }); 655 656 jnlpd.setMainClass(mainclass, true); 657 jnlpd.setName(name); 658 } 659 }