X

Geertjan's Blog

  • March 25, 2008

org.openide.xml.XMLUtil

Geertjan Wielenga
Product Manager
I suddenly realized that my ruminations on org.openide.xml.XMLUtil, yesterday, might be helpful to someone I met at the NetBeans booth at Sun Tech Days in Johannesburg, a few weeks ago. He wanted to create a NetBeans plugin that would generate language-specific client stubs from a WSDL file. The basic concept is that you would open the WSDL file in the IDE, right-click inside it, and then choose a menu item that says, something like, "Generate Client Stubs". And then you'd get a new HTML file with a list of client stubs for interacting with the service exposed via the WSDL. You'd get a stub in JavaScript, in C++, in Java, and in anything else that's relevant. You, as the provider of the WSDL, would then post your WSDL on your server and then ALSO post the HTML file with client stubs. The user of your WSDL would then have a starting point, for whatever language they're coding in.

I thought it was a great suggestion and coded a basic plugin as a starting point, which he copied onto his USB stick. I could have gone a lot further than I did, had I known about org.openide.xml.XMLUtil at the time. So, here's complete instructions for creating your own language-specific client stubs from a WSDL file:

  1. Create a new module project, name it whatever you like.
  2. Make sure the distro of NetBeans IDE that you are using has specific support for WSDL files (i.e., to check this, create a file with a WSDL extension or open one with that extension, and then see if a special icon is shown for it and that the editor has lots of WSDL-specific support or not). If not, go to the Plugin Manager, search for "wsdl" and then install the WSDL plugin.
  3. Create a class that extends CookieAction. (If you use the Action wizard, specify that the action should be conditionally enabled, for the EditorCookie class, and the text/x-wsdl+xml MIME type under "Editor Context Menu Item").
  4. Registration of the class in the layer.xml would probably be like this:
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE filesystem PUBLIC "-//NetBeans//DTD Filesystem 1.1//EN" "http://www.netbeans.org/dtds/filesystem-1_1.dtd">
    <filesystem>
    <folder name="Actions">
    <folder name="Tools">
    <file name="org-netbeans-webservicemenuitem-SomeAction.instance"/>
    </folder>
    </folder>
    <folder name="Editors">
    <folder name="text">
    <folder name="x-wsdl+xml">
    <folder name="Popup">
    <file name="org-netbeans-webservicemenuitem-SomeAction.shadow">
    <attr name="originalFile" stringvalue="Actions/Tools/org-netbeans-webservicemenuitem-SomeAction.instance"/>
    <attr name="position" intvalue="0"/>
    </file>
    </folder>
    </folder>
    </folder>
    </folder>
    </filesystem>

  5. Define the CookieAction.performAction as follows, taking note of the line in bold, as well as all the FQN for DOM objects (because that's the point of this blog entry):
    protected void performAction(Node[] activatedNodes) {
    try {
    InputOutput io = IOProvider.getDefault().getIO("WSDL Elements and Attributes", true);
    EditorCookie ec = activatedNodes[0].getLookup().lookup(EditorCookie.class);
    StyledDocument sd = ec.getDocument();
    String text = sd.getText(0, sd.getLength());
    InputSource source = new InputSource(new StringReader(text));org.w3c.dom.Document doc = XMLUtil.parse(source, false, false, null, null);
    org.w3c.dom.NodeList list = doc.getElementsByTagName("\*");
    int length = list.getLength();
    io.select();
    for (int i = 0; i < length; i++) {
    org.w3c.dom.Node mainNode = list.item(i);
    org.w3c.dom.NamedNodeMap map = mainNode.getAttributes();
    String nodeName = mainNode.getNodeName();
    StringBuilder attrBuilder = new StringBuilder();
    for (int j = 0; j < map.getLength(); j++) {
    org.w3c.dom.Node attrNode = map.item(j);
    String attrName = attrNode.getNodeName();
    attrBuilder.append(" --> "+ attrName);
    }
    io.getOut().println("ELEMENT: " + nodeName + " --> ATTRIBUTES: " + attrBuilder.toString());
    }
    io.getOut().close();
    } catch (IOException ex) {
    Exceptions.printStackTrace(ex);
    } catch (SAXException ex) {
    Exceptions.printStackTrace(ex);
    } catch (BadLocationException ex) {
    Exceptions.printStackTrace(ex);
    }
    }

    The above will print out the elements and attributes in your WSDL file to the Output window:

    That's pretty cool. Because now you have the basis of your client stub generator. After all, you can now simply use everything that the org.w3c.dom package gives you to parse the output and, once you have the elements that you want, you can use StyledDocument.insertString(offset, string, attributeSet) to insert whatever you want into the editor. Alternatively, create a new FileObject (such as an HTML file) and then write whatever you want into it.

    In other words, the first benefit of org.openide.xml.XMLUtil is that its parse method returns a plain old org.w3c.dom.Document, which you can manipulate however you like using the JDK's org.w3c.dom package.


  6. However, let's look more closely at the XMLUtil.parse method. First of all, you can hook your own org.xml.sax.ErrorHandler into the parser, very easily:
    org.w3c.dom.Document doc = XMLUtil.parse(source, false, false, new WSDLParseErrorhandler(), null);

    And then create your own org.xml.sax.ErrorHandler, while benefiting from the IDE's code completion:

    Here's a very handy org.xml.sax.ErrorHandler, which writes error messages to the Output window:

    class WSDLParseErrorhandler implements org.xml.sax.ErrorHandler {
    InputOutput io = IOProvider.getDefault().getIO("WSDL Errors", true);
    private void generateOutput(SAXParseException exception, String type) {
    int line = exception.getLineNumber();
    String message = exception.getMessage();
    io.select();
    io.getOut().println(type + " in line " + line + ": " + message);
    }
    public void warning(SAXParseException exception) throws SAXException {
    generateOutput(exception, "Warning");
    }
    public void error(SAXParseException exception) throws SAXException {
    generateOutput(exception, "Error");
    }
    public void fatalError(SAXParseException exception) throws SAXException {
    generateOutput(exception, "Fatal error");
    }
    }

  7. Let's now look at another argument to the XMLUtil.parse method. Simply change the first false boolean to true:
    org.w3c.dom.Document doc = XMLUtil.parse(source, true, false, new WSDLParseErrorhandler(), null);

    By setting that argument to true, your WSDL file (or whatever file you're working with, of course) will be validated against its DTD declaration or Schema declaration. And now make sure to declare a DTD or Schema to which your WSDL file does not comply. For example, I simply cut and pasted the DTD reference at the top of my layer.xml file into my WSDL file. Obviously, that's not going to resolve very well (or, at all):


  8. The final argument to the XMLUtil.parse method is amply described in the related Javadoc. The point is that even if you have set the first boolean to false, i.e., even if you do NOT want to validate, the ui will still be blocked if you have a DTD declaration or Schema declaration. The ui will be blocked because a network connection will be made, based on the URL specified in the DTD declaration or Schema declaration. You can speed up parsing by defining an entity resolver, as described in the related Javadoc.
  9. And what about the second boolean? That, if true, makes your parser namespace aware. As this document tells you: "A parser that is namespace aware can recognize the structure of names in a namespace-with a colon separating the namespace prefix from the name. A namespace aware parser can report the URI and local name separately for each element and attribute. A parser that is not namespace aware will report only an element or attribute name as a single name even when it contains a colon. In other words, a parser that is not namespace aware will treat a colon as just another character that is part of a name."

At this point, we haven't really examined org.openide.xml.XMLUtil at all. We've only looked at its parse method. In other words, there's a lot more that this class can give you. But, clearly, already, it is clear that the org.openide.xml.XMLUtil class is a very useful addition to the NetBeans Platform indeed!

Join the discussion

Comments ( 2 )
  • Maxx Monday, April 28, 2008

    Hi. Can I somehow use this library outside Module project, in my only java application? Thanks


  • Geertjan Monday, April 28, 2008

    I haven't tried that, but I think it should be possible Maxx.


Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.