Monday Mar 31, 2008

NetBeans IDE of the Future

I attended an interesting meeting yesterday. NetBeans hosted some of the leading thinkers from NASA, MIT, and Mensa. We ended up brainstorming the "NetBeans of the Future". Those Mensa guys, especially, were pretty smart. And MIT's involvement was really helpful too because they were able to add a level of practicality to the discussions. They really know the NetBeans APIs well, they're so smart with their photographic memory that they just need to scroll through the Javadoc once and then they know everything they'll ever need for the most complex applications, without needing to refer back to the Javadoc ever again.

Anyway, what was the result of the discussions? "NetBeans of the future", or "NOTF", will be thought-control based. What that means is that you'll be able to think "Click File | New Project". And then NetBeans IDE will take that command and choose File | New Project. One might think that this is a nice idea but not very practical. However, again, the super smart guys from MIT were able to immediately code up a prototype. Within 10 minutes. Then they demoed it. One guy thought up a "Hello world" Java application and we could see it being created in NetBeans IDE, without him touching his laptop at all.

So, that was the first half hour of the meeting.

In the second half hour, we created "Dream Batch Processes", or "DBPs", which are programming sequences that can be injected into your dreams, so that you can be coding in NetBeans IDE while you are sleeping. The DBP is cutting edge stuff and NASA might reimplement it in their space technology. They will use monkeys first. However, the DBPs were particularly promising, said one Mensa participant at the meeting, because it would result in guys creating really sexy looking user interfaces in their dreams.

Watch this space for further developments. Thanks to NASA, MIT, and Mensa, for participating.

Sunday Mar 30, 2008

OpenOffice.org... has its own Plugin Portal!

The OpenOffice.org developers of the NetBeans plugin for OpenOffice.org are in Prague today. I attended an interesting presentation where they demoed their plugin this morning. One of several things I learned about was their very cool Extensions site:

http://extensions.services.openoffice.org/

Unfortunately, however, you cannot see (currently) from inside OpenOffice.org which extensions are available. On the other hand, this is a feature that NetBeans IDE 6.1 Beta does have support for (at least, on my machine, thanks to a plugin I created) because I integrated their various application-specific extension feeds into the IDE's toolbar:

When I get some time, I will try to create an OpenOffice.org Add-On (using the NetBeans/OpenOffice.org plugin) which will attempt to do the same thing from within OpenOffice.org.

Here's the relevant code in the JPanel, which I added to the toolbar via CallableSystemAction.getToolbarPresenter:

public class OOExtensionsPanel extends javax.swing.JPanel {

    DefaultComboBoxModel model = new DefaultComboBoxModel();

    /\*\* Creates new form OOExtensionsPanel \*/
    public OOExtensionsPanel() {
        initComponents();
        //appList is the list of OpenOffice.org applications:
        appList.addActionListener(new AppListListener());
        //extList is the list of OpenOffice.org extensions:
        extList.setModel(model);
    }

    private class AppListListener implements ActionListener {

        public void actionPerformed(ActionEvent ev) {

            model.removeAllElements();

            BufferedReader in = null;
            
            try {

                //InputOutput io = IOProvider.getDefault().getIO("OpenOffice Extensions", true);
                JComboBox box = (JComboBox) ev.getSource();
                String selectedApp = box.getSelectedItem().toString();

                URL url = null;
                if (selectedApp.equals("Writer")) {
                    url = new URL("http://extensions.services.openoffice.org/taxonomy/term/2/0/feed");
                } else if (selectedApp.equals("Calc")) {
                    url = new URL("http://extensions.services.openoffice.org/taxonomy/term/1/0/feed");
                } else if (selectedApp.equals("Draw")) {
                    url = new URL("http://extensions.services.openoffice.org/taxonomy/term/4/0/feed");
                } else if (selectedApp.equals("Impress")) {
                    url = new URL("http://extensions.services.openoffice.org/taxonomy/term/3/0/feed");
                } else if (selectedApp.equals("Chart")) {
                    url = new URL("http://extensions.services.openoffice.org/taxonomy/term/21/0/feed");
                }

                in = new BufferedReader(new InputStreamReader(url.openStream()));
                InputSource source = new InputSource(in);
                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);
                    String nodeName = mainNode.getNodeName();
                    String value = mainNode.getTextContent();
                    if (nodeName.equals("title")) {
                        //io.getOut().println("Extension: " + value);
                        model.addElement(value);
                    }
                }
            //io.getOut().close();

            } catch (SAXException ex) {
                Exceptions.printStackTrace(ex);
            } catch (IOException ex) {
                Exceptions.printStackTrace(ex);
            } finally {
                try {
                    in.close();
                } catch (IOException ex) {
                    Exceptions.printStackTrace(ex);
                }
            }
        }
    }
...
...
...

Today on NetBeans Zone. Overview of NetBeans Java API Samples in Plugin Portal.

Saturday Mar 29, 2008

Extension to Extended Java Project (Part 2)

I published version 2 of the Extended Java Project Sample. There is one very big improvement: FreeMarker is used. As a result, you can perform iterations and other code logic right within the template, instead of needing to do so in the code. There's also a lot less code in general in the sample now. One reason for that is that I discovered a simpler way of accessing a file inside the module's sources. This is how the WordLibrary.ftl file is located, instead of reading input streams and so on:

URL wordLibraryClass = new URL("nbfs:/SystemFileSystem/Templates/Project/Standard/file/WordLibrary");

The "nbfs" protocol lets you get files from the System FileSystem! You can register the files you need your module to have access to like shown below (register the file anywhere in the System Filesystem, but probably preferable to put it very close to the resources that will be needing it, as in this case, where I put the file very close to the project template that needs it):

<folder name="Templates">
    <folder name="Project">
        <folder name="Standard">
            <file name="MyAnagramGameProject.zip" url="MyAnagramGameProject.zip">
                <attr name="SystemFileSystem.icon" urlvalue="nbresloc:/org/netbeans/modules/java/j2seproject/ui/resources/j2seProject.png"/>
                <attr name="SystemFileSystem.localizingBundle" stringvalue="org.nb.extendedjavaproject.Bundle"/>
                <attr name="instantiatingIterator" methodvalue="org.nb.extendedjavaproject.MyAnagramGameWizardIterator.createIterator"/>
                <attr name="instantiatingWizardURL" urlvalue="nbresloc:/org/nb/extendedjavaproject/MyAnagramGameDescription.html"/>
                <attr name="template" boolvalue="true"/>
            </file>
            <folder name="file">
                <file name="WordLibrary" url="WordLibrary.ftl">
                    <attr name="template" boolvalue="true"/>
                    <attr name="javax.script.ScriptEngine" stringvalue="freemarker"/>
                </file>
            </folder>
        </folder>
    </folder>
</folder>

The layer.xml file and the WordLibrary.ftl file are in the same directory, hence the url attribute above is simply set to WordLibrary.ftl.

And so now I can simply do this:

private FileObject insertSpecifiedWords() throws IOException {

    //Get the folder where the files should be created,
    //note that we don't give the user an option here:
    File projectFolder = FileUtil.normalizeFile((File) wiz.getProperty("projdir"));
    FileObject targetFolder = FileUtil.toFileObject(new File(projectFolder + "/src/com/toy/anagrams/lib"));
    DataFolder df = DataFolder.findFolder(targetFolder);

    //Find the template in the System Filesystem:
    URL wordLibraryClass = new URL("nbfs:/SystemFileSystem/Templates/Project/Standard/file/WordLibrary");

    //Map the template to a FileObject:
    FileObject fo = URLMapper.findFileObject(wordLibraryClass);

    //Convert to a DataObject:
    DataObject dTemplate = DataObject.find(fo);

    //Get the unscrambled words from the panel:
    String[] unscrambleds = new String[]{
        (String) wiz.getProperty("unScrambledWord1"),
        (String) wiz.getProperty("unScrambledWord2"),
        (String) wiz.getProperty("unScrambledWord3"),
        (String) wiz.getProperty("unScrambledWord4"),
        (String) wiz.getProperty("unScrambledWord5"),
        (String) wiz.getProperty("unScrambledWord6"),
        (String) wiz.getProperty("unScrambledWord7")
    };

    //Get the scrambled words from the panel:
    String[] scrambleds = new String[]{
        (String) wiz.getProperty("scrambledWord1"),
        (String) wiz.getProperty("scrambledWord2"),
        (String) wiz.getProperty("scrambledWord3"),
        (String) wiz.getProperty("scrambledWord4"),
        (String) wiz.getProperty("scrambledWord5"),
        (String) wiz.getProperty("scrambledWord6"),
        (String) wiz.getProperty("scrambledWord7")
    };

    //Create a map for passing the words to the FreeMarker template,
    //where it will be processed:
    Map args = new HashMap();
    args.put("unscrambleds", unscrambleds);
    args.put("scrambleds", scrambleds);

    //Create a new DataObject from the template that we defined above,
    //by providing a folder, the name of the file to be created,
    //and the map that should be passed to the FreeMarker template for processing:
    DataObject dobj = dTemplate.createFromTemplate(df, "WordLibrary.java", args);

    //Get the FileObject from the DataObject:
    FileObject createdFile = dobj.getPrimaryFile();

    //Return the FileObject:
    return createdFile;

}

And then in the instantiate method, I add the above to the result set.

And, thanks to FreeMarker, I can send in my two string arrays via a map, constructed above, which I can then iterate through (using syntax coloring and code completion from my own module which is in the Plugin Portal) in the template:

Pretty cool. But the best trick or, at least, the only one that was news to me, was the thing with the "nbfs" protocol. That provides a very easy way to access files in the System FileSystem, including those that you register there yourself, of course.

Integrating Ubuntu into NetBeans IDE

I like Ubuntu's text editor, especially for XML files it's great—gives you very nice syntax coloring. So I created a menu item on XML file nodes in NetBeans IDE, especially for opening an XML file into Ubuntu's text editor. That way, I simply have an extra window for editing XML. Not super useful functionality, but handy sometimes.

To create this action, use the New Action wizard to create a new conditional action (i.e., not "always enabled"), for DataObjects. Specify that it should be invoked from the text/xml-mime MIME type. Then set dependencies on Datasystems API, Execution API, File System API, Nodes API, and Utilities API.

Then fill out the CookieAction.performAction as follows, taking note of the line in bold below, because that's where all the action happens:

protected void performAction(Node[] activatedNodes) {
    try {
        DataObject dataObject = activatedNodes[0].getLookup().lookup(DataObject.class);
        FileObject fo = dataObject.getPrimaryFile();
        String file = fo.getURL().getFile();
        try {
            NbProcessDescriptor desc = new NbProcessDescriptor("gedit", file);
            desc.exec();
        } catch (IOException ex) {
            JOptionPane.showMessageDialog(null, ex);
        }
    } catch (FileStateInvalidException ex) {
        Exceptions.printStackTrace(ex);
    }
}

You should be using the following import statements:

import java.io.IOException;
import javax.swing.JOptionPane;
import org.openide.execution.NbProcessDescriptor;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileStateInvalidException;
import org.openide.loaders.DataObject;
import org.openide.nodes.Node;
import org.openide.util.Exceptions;
import org.openide.util.HelpCtx;
import org.openide.util.NbBundle;
import org.openide.util.actions.CookieAction;

And that's it. Install the module and notice the new menu item on XML files (mine is called "Open in Text Editor"). When the menu item is selected, the selected XML file opens in Ubuntu's (or any other Linux or whatever system) text editor (or some other application), displaying your file.

This is how you would open a file programmatically in OpenOffice.org, just replacing the important line above with this one:

NbProcessDescriptor desc = new NbProcessDescriptor("ooffice", " -writer " +file);

I tried to open a file in both Eclipse and IntelliJ, using the above line of code, but—even though both applications start up successfully—in neither case does the file open when the application has started up. Maybe there's a special command line for opening a file in Eclipse or IntelliJ when they start up? (Something like -file fileName.) But I suspect that, unlike NetBeans IDE, they don't have an option like that. It seems to me that the other two IDEs are not able to have a specific file be opened on start up, based on a command line setting specified at start up.

Friday Mar 28, 2008

Extension to Extended Java Project (Part 1)

A few days ago, based on a request from a user, I published a sample to the Plugin Portal to show how a project template can be extended with a new panel. Now I've extended it to do what the user probably really wants to do: use the new panel to generate a file which, in this case, is a class.

The plugin that you get from the Plugin Portal will, once installed, give you a new plugin sample in the NetBeans Modules category in the New Project wizard. Once you install that, you'll have a new Java application that generates the Anagram Game. However, there's a new panel, where you can set the scrambled/unscrambled word pairs:

So, once you click Finish, you will have the whole Anagram Game. However, the WordLibrary.java file will not have the words that are normally found there, i.e., "batsartcoin", etc. Instead, the words set in the panel above will be used to populate those parts of the WordLibrary.java file:

General instructions for creating something similar yourself are provided on the same page where you can get the plugin:

http://plugins.netbeans.org/PluginPortal/faces/PluginDetailPage.jsp?pluginid=6743.

Thursday Mar 27, 2008

Java Hint Generator for NetBeans IDE 6.1 Beta

I've blogged about hints before and about how cool it would be for users out there to create hints so as to increase (even further) the power of the NetBeans Java Editor. But now it's a simplified process:

http://plugins.netbeans.org/PluginPortal/faces/PluginDetailPage.jsp?pluginid=6784

Install that plugin and read the accompanying instructions.

Here's where you'll find the new template, once you've installed the above plugin:

Tuesday Mar 25, 2008

Resizing Images in NetBeans IDE

Small(ish) but annoying pet peeve of mine: I need an image for an application that I'm coding in NetBeans IDE. I go on-line and find the image that I need. I right-click the image on-line and save it right there, from my browser, into my application's source structure.

Then... dang! I open the image in the IDE and see that... its size is not what I would want it to be. So... I need to go outside the IDE (where it is cold, cold, cold) and change the image size there. Why? Because when you open the image in the IDE, you can only zoom. Not resize.

So, I've added a "Resize" menu item on image nodes in the explorer views:

When the menu item is selected, the image opens in a new dialog, which should be a TopComponent at some point, where you can resize the image (thanks to the Visual Library):

When you click OK, a new image is created (so, no worries, nothing will happen to your original image), in the same place as the old one, with a new name, consisting of the original name, plus the dimensions of your new image.

It is not perfect yet, but it is definitely already usable in most cases:

http://plugins.netbeans.org/PluginPortal/faces/PluginDetailPage.jsp?pluginid=6726

If your original image is very large and you want to make it smaller, you'll have problems, because the original image and the image you're resizing will overlap. As the image you're resizing gets smaller, you won't be able to see it anymore because the original image will cover it. Need to fix that. It works better if you want to make the image larger than the original.

The latest version of the plugin (1.2) includes antialiasing, but the quality of the image will definitely decrease as you increase its size. Advice on this point is very welcome.

Monday Mar 24, 2008

org.openide.xml.XMLUtil

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!

Hello Spring (Part 2)

The cool thing about the new Spring support in NetBeans IDE 6.1 is the fact that you can extend it. A very simple entry point into the Spring support is its dedicated MIME type, x-springconfig+xml. Via this MIME type, you can add a new menu item, either to the editor or to the node in the explorer view, specifically for Spring configuration files. Using this technique, I added a menu item called "Generate Java from Beans":

What happens when the menu item is selected? You get some Java code generated at the bottom of the file, for accessing each bean, which you can then copy into your Java classes, wherever the code is needed. Maybe it would be nice if this were integrated into the Java classes, so that the beans would appear during code completion, but that's a couple of bridges too far for me at the moment, I'd need to do a bit of research for that. Currently I'm only supporting the util:list bean, but it would be simple to expand this to all other types. Here's the result for the above two beans, i.e., everything that is commented out below is generated when the menu item above is selected:

And here's the relevant performAction in my CookieAction. Take particular note of org.openide.xml.XMLUtil which, if you're not aware of it, is very useful indeed (even more so if you're aware of it):

protected void performAction(Node[] activatedNodes) {

    //Figure out the name of the configuration file,
    //plus its packages, plus "src", 
    //which is what the org.springframework.core.io.FileSystemResource class needs:
    DataObject dobj = activatedNodes[0].getLookup().lookup(DataObject.class);
    FileObject fo = dobj.getPrimaryFile();
    setFileName(fo.getPath().substring(fo.getPath().indexOf("src")));

    try {
        //Figure out the document and parse it:
        EditorCookie editorCookie = activatedNodes[0].getLookup().lookup(EditorCookie.class);
        StyledDocument styledDoc = editorCookie.openDocument();
        String allText = styledDoc.getText(0, styledDoc.getLength());
        InputSource source = new InputSource(new StringReader(allText));
        //No validation, not namespace aware, no entity resolver, no error handler:
        Document doc = XMLUtil.parse(source, false, false, null, null);

        //Figure out the list of elements:
        NodeList list = doc.getElementsByTagName("\*");
        int docLength = list.getLength();
        for (int i = 0; i < docLength; i++) {
            org.w3c.dom.Node node = list.item(i);

            //Figure out the list of attributes:
            NamedNodeMap map = node.getAttributes();
            int mapLength = map.getLength();
            for (int j = 0; j < mapLength; j++) {
                org.w3c.dom.Node attr = map.item(j);

                //Insert the template, with values filled in,
                //if the attribute is "id":
                if (attr.getNodeName().equals("id")) {
                    setBeanId(attr.getNodeValue());
                    styledDoc.insertString(styledDoc.getLength(), 
                            "\\n<!-- How to access the \\"" + attr.getNodeValue() +
                            "\\" bean:\\n" + getTemplate(), null);
                }

            }
        }
    } catch (SAXException ex) {
        Exceptions.printStackTrace(ex);
    } catch (BadLocationException ex) {
        Exceptions.printStackTrace(ex);
    } catch (IOException ex) {
        Exceptions.printStackTrace(ex);
    }

}

And, FWIW, here's the template:

private String getTemplate() {
    template =
            "\\nBeanFactory factory = new XmlBeanFactory(new FileSystemResource(\\"" + getFileName() + "\\"));" +
            "\\nArrayList list = (ArrayList) factory.getBean(\\""+getBeanId()+"\\");" +
            "\\nIterator it = list.iterator();" +
            "\\nint count = 0;" +
            "\\nwhile (it.hasNext()) {" +
            "\\n    count = count + 1;" +
            "\\n    System.out.println(\\""+getBeanId()+" \\" + count + \\": \\" + it.next().toString());" +
            "\\n}" +
            "\\n-->\\n";
    return template;
}

Sunday Mar 23, 2008

Hello Spring (Part 1)

My first encounter with Spring is through its Util schema. I used NetBeans IDE 6.1 Beta, since this version of NetBeans IDE sports a set of features specifically for Spring, for the first time. There's support for web applications (which I will discuss in a future blog entry), but also for Java SE applications. Here's my first Spring configuration file in a Java SE application:

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
       http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
       http://www.springframework.org/schema/util 
       http://www.springframework.org/schema/util/spring-util-2.5.xsd">
           
    <util:list id="emails">
        <value>john@smith.org</value>
        <value>jack@harry.org</value>
        <value>peter@piper.org</value>
        <value>pavel@prochazka.org</value>
    </util:list>
    
</beans>

To help me while coding the above file, I have context-sensitive code completion to help me:

And here's how I created it, i.e., using a new template...

... which also lets me choose one or more namespaces, so that I don't need to think about the header of the Spring configuration file at all:

Here's my simple Java class for accessing (and using) the above Spring configuration file:

package hellospring;

import java.util.ArrayList;
import java.util.Iterator;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.FileSystemResource;

public class Main {

    public static void main(String[] args) {
        BeanFactory factory = new XmlBeanFactory(new FileSystemResource("src/hellospring/demo.xml"));
        ArrayList list = (ArrayList) factory.getBean("emails");
        Iterator it = list.iterator();
        int count = 0;
        while (it.hasNext()) {
            count = count + 1;
            System.out.println("Email " + count + ": " + it.next().toString());
        }
    }
    
}

I am also able to get Java code completion in my Spring configuration file, whenever I need it, specifically for class attributes:

And then I can simply click the class references, which then results in the class being opened in the editor:

Finally, I can organize my Spring configuration files. On the Project Properties node of Java SE applications, there's a new node that appears when the Spring JARs are on my classpath, for grouping my Spring configuration files:

Finally, for more info on the new Spring support, see Improved Spring Framework Support in NetBeans 6.1: XML-Config Files in Ramon Ramos's blog.

Tuesday Mar 18, 2008

Brand New Template for NetBeans Platform Applications

Very recent 6.1 development builds should bring joy to those of us who have long argued for improvements in the discoverability of tools for NetBeans Platform development. There is now a brand new template for this purpose:

When you complete the wizard, you have an empty application, i.e., unlike the module suite template, which comes with all the IDE's modules installed. Simply run it, without doing anything special beforehand, and you have an empty application, with all the basic bells and whistles (i.e., menus and toolbars) available:

Many thanks to Jesse for creating this template and to Ondrej for recently arguing the need for its creation.

Pluggable Photo Album Ported to the NetBeans Platform

A few days ago I blogged about using the NetBeans Lookup API outside the NetBeans Platform. I referred to the very handy Creating Extensible Applications With the Java Platform, by John O'Conner, and then wrote How to Create a Pluggable Photo Album in Java on Javalobby. The next logical step was to move my pluggable photo album to the NetBeans Platform:

Now, instead of three Java applications, I have three modules. The first (module 1) provides the TopComponent, an SPI for photos, and a service that acts as a bridge between the SPI and the providers (module 2 and module 3). The photos are NOT in module 1. They come from module 2 and module 3. Below you see the content of module 2, which is the same structure as module 3. Both implement the Photo class provides by module 1. They also use the META-INF/services folder to register their Photo implementation:

Next, I will explore the special features that the Lookup class provides over those provided by the JDK 6 ServiceLoader class. For example... the LookupListener class, which should be very interesting to examine in this context. And I am expecting other benefits too.

By the way, I highly recommend the procedure I used to get to this point: take a NetBeans API through its paces outside the NetBeans Platform, where possible, and then port the application to the NetBeans Platform to reap the additional rewards that the NetBeans Platform gives you.

Monday Mar 17, 2008

A different way of starting the IDE

Drag and drop a desktop icon of a file onto the NetBeans desktop icon...

...and the IDE will start up, with the specified file open in the editor. That's always been possible, but is [as far as I am aware] completely unknown.

Saturday Mar 15, 2008

Lookup API Outside the NetBeans Platform

Today an e-mail came in to the dev@openide.netbeans.org mailing list (the place you want to be if you're interested in the NetBeans Platform or in NetBeans plugin development generally) from Mark Nuttall. He asks: "Where do I snag the current Lookup API from if I want to use it outside of the NetBeans Platform." The short answer to that question is: go to the IDE's installation directory and then to the platform8/lib folder, where you will find the org-openide-util.jar. Just attach that JAR to your application's classpath and you're good to go.

The longer answer is... even better! On java.sun.com, there is an excellent article by John O'Conner, Creating Extensible Applications With the Java Platform. Not only does he describe the benefits of the Lookup API, and how to use it outside of the NetBeans Platform, but he also contrasts it to the Java SE 6 java.util.ServiceLoader class. (And, if you read Rich Client Programming: Plugging into the NetBeans Platform, you'll find out lots about these contrasts too, in chapter 4 and 5.)

However, I can't recommend John's article highly enough. He shows you how to build a pluggable application (outside of the NetBeans Platform) using both the Java SE 6 approach and the NetBeans Platform approach (and discusses the differences between the two). The article includes the complete sample code, everything, and, in fact, you end up with two different implementations of a Dictionary application. Below you see the result, i.e., everything in the screenshot below comes straight from the downloadable sources from John's article. So you end up with three applications, the first providing the user interface, and the other two are registered services (I have also highlighted the Lookup API JAR below so that you can see its name):

Anyone new to, or confused about, Lookup is advised to read the above article, download the sources, play with them, see how they relate to each other, read the article again, and then try and make something similar yourself to see if you really understand it. The NetBeans Lookup API isn't discussed to its full extent and so reading chapter 4 and 5 of "Rich Client Programming: Plugging into the NetBeans Platform" is a very worthwhile thing to do after you've understood everything that John's article provides. It is basically an excellent primer on loosely coupled communication between a system's components.

In other news. Check out the NetBeans blogging contest!

Deployment History Window (Part 2)

My Deployment History plugin is basically finished. I think it is self explanatory from the screenshot below. The nice thing is that an application can even be redeployed if it isn't open in the IDE anymore:

However, note that the latest version of the application is always run (i.e., I don't save the application as such, so you can't go back in time and run an earlier version of your application, which would be cool and is something I'll investigate). I'm going to add some more features, such as the ability to clear individual items from the list and to clear the whole list in one go. It would also be pretty cool if the entries could be stored via NbPreferences and then reloaded upon restarts. Thanks a lot, Gareth Uren, for suggesting this plugin while visiting the NetBeans booth at Sun Tech Days in Johannesburg!

In other news. Join Roumen and others at the NetBeans party at EclipseCon! I wonder what will happen there. Maybe Brian will do his demo of how to convert Eclipse applications to NetBeans projects, while pretending that his presentation is an official sequel to one of the EclipseCon presentations? (He could start like this: "In part 1, you learned how to create some cool applications in Eclipse. Now, in part 2, we'll look at how to get them where they will feel much happier—NetBeans IDE!") Maybe Roumen will pull one of his notorious pranks? Should be interesting, whatever happens.

Friday Mar 14, 2008

Deployment History Window (Part 1)

I'm halfway done with Gareth Uren's suggestion, as recorded in my blog entry yesterday: "a drop down button factory displaying recently run projects that can then be selected and then immediately run again". It isn't implemented in a drop down button factory, but a window. And the entries in the window can't be redeployed yet (so, right now, only a new entry is generated upon deployment, no redeployment can be peformed yet). However, it's pretty cool to see the deployed app's name (and deployment time) added to the window upon deployment:

How is this done? Mainly via an Ant Logger. It's a little bit hacky, but I think it should do the job in 99% of cases. I locate the project.xml file, where the project name is set, and then identify the <name> element, which is the element that defines the name. That's all. The most important method, where everything happens, is the targetStarted method in the org.apache.tools.ant.module.spi.AntLogger class:

@Override
public void targetStarted(AntEvent event) {
    BufferedReader br = null;
    try {
        //Identify the build-impl.xml file:
        File buildImplXML = event.getScriptLocation();
         //Hacky approach to getting the project.xml file instead:
        File projectXML = new File(buildImplXML.getCanonicalPath().replace("build-impl.xml", "project.xml"));
        //Read the project.xml file:
        br = new BufferedReader(new InputStreamReader(new FileInputStream(projectXML)));
        //Get the target's name, so we can test if it is the "run" target or not:
        String name = event.getTargetName();
        while ((inputLine = br.readLine()) != null) {
            //If the target name is "run" and the line starts with <name>:
            if (name.equals("run") && inputLine.trim().startsWith("<name>")) {
                //Strip the line for the project name:
                String projectName = inputLine.trim().replace("<name>", "").replace("</name>", "");
                //Get today's date:
                java.util.Date today = new Date();
                //Create a timestamp:
                Timestamp stamp = new java.sql.Timestamp(today.getTime());
                //Send everything to the TopComponent, where the "set" method populates the JTextArea:
                ListDeployedAppsTopComponent.setProjectNames(projectName + "\\n (at" + stamp +")\\n");
            }
        }
    } catch (IOException ex) {
        Exceptions.printStackTrace(ex);
    } finally {
        try {
            br.close();
        } catch (IOException ex) {
            Exceptions.printStackTrace(ex);
        }
    }
}

The next step is to let the user double-click an entry. When this is done, a Run target will be executed, with the project name as the application to be run. Or, at least, that's my plan.

Thursday Mar 13, 2008

Sun Tech Days, Day 2, Johannesburg, 2008

Another pretty excellent day. I basically spent the entire day at the booth. Sang had asked me to join him in his hands on lab on performance and profiling for web applications, because of that topic's connection to VisualVM. He proposed that I would spend some of the hands on lab introducing VisualVM. I didn't think I'd be able to say that much about it (it is, after all, a visual application, hence spending a lot of time talking about it seems slightly beside the point), but then I thought: "What if I show them how easy it is to create plugins for VisualVM?"

That was a cool idea, but then I needed to make sure that it really IS easy, otherwise I'd be undermining VisualVM completely (imagine if I were to say "it is really easy to extend VisualVM" and then were to spend the next half hour going through some complex procedure with many twists and turns). So I spent most of the day at the booth working on what became the VisualVM View Template. Because of the many interesting discussions that kept on arising due to people stopping by to ask something about NetBeans, it was only during Sang's part of the hands on lab (which was the last session of the day) that I actually finished the template and uploaded it to the Plugin Portal. Right after that I used it live in my presentation! There were not very many people, but I think it was a good opportunity to show that it really is easy to slide your own JPanel into a plugin's newly created VisualVM tab. (I used the Memory Monitor JPanel from the JDK's demo/management folder as an example, installing it into VisualVM, typing only a single line to do so.) People were pretty impressed with the GlassFish plugin. One of them wants to start working on a WebSphere plugin. That would be pretty cool.

Apart from that, i.e., before Sang's hands on lab, I had many conversations with users. One of them said, literally: "Whenever I open NetBeans IDE, I feel like applauding." (She knows about my blog and so if I am lying, she'll be sure to leave a comment saying so!) One of the most interesting ideas came from someone called Gareth Uren who asked for a drop down button factory displaying recently run projects that can then be selected and then immediately run again (which is something he missed from having used Eclipse). I agree, that would be a good thing. I wonder if there is an API that can be used to detect whether/which applications have been (successfully) deployed. If there is such a thing, it would be easy to put the name of those deployed applications in a drop down button. Then, somehow, the link between the name in the list and the actual application needs to be maintained so that the application can be redeployed. But I wouldn't be surprised if it were doable and I'd like to investigate this.

Someone else is working on an extension to WSDL files. I showed him how to add a menu item to the WSDL file's contextual menu item. And also how to get the editor's content from that menu item. He wants to use that starting point as a means to generating stubs for various clients (PHP, Java, C++, etc), based on the WSDL file's content. And that would then be displayed in an HTML page (or in a new window) or something like that. Sounds like an interesting plan. He had some web services running on his own site and I showed how to create clients (both desktop and web) with a very few clicks, with the results put in the Output window. I also demoed quite a bit of other web service related activities, some with my favorite Shakespeare web service (which retrieves texts based on a phrase you send it). People were really impressed by how easy it is to create web service clients (and showing the drag/drop of web service methods into the editor is always a winner).

I met quite a few people from Sun, in one way or another. Especially the Java CAPS people, across the room from the NetBeans booth, were interesting. They showed me how Java CAPS now runs on a very recent NetBeans IDE 6.1 development build! They were very positive about the NetBeans Platform. I arranged to do an interview with one of them sometime and also to put a screenshot of their application on the NetBeans Platform screenshots page, to show yet another example of the power of the NetBeans Platform.

I've received a couple of e-mails from some of the people I've met, reiterating the enhancement ideas they have for NetBeans IDE. (One of them is going to create new hints for the NetBeans Java editor!) I'll blog about those sometime soon I hope. Quite a lot of people were introduced to my blog for the first time, so I'm looking forward to hearing from them! After the hands on lab with Sang Shin was over, I went with Schalk Neethling (also a DZone leader) and a few of the others for a beer across the street. Rick Ross told me some days ago that he would pay for all our drinks if Schalk and I ended up hanging out with some other developers and, well, Rick, here's the evidence:

And then I had dinner with some colleagues. It was a pretty good day. I met very many nice and enthusiastic NetBeans users. I do believe that Dale wins the prize for most enthusiastic NetBeans user, since he spent so much time at our booth! Hoping to meet the people I met again when/if the next Sun Tech Days happens here again! On top of all that, when I got back to my hotel, I found that I had been upgraded to a much better room. Cool ending to a great day.

Wednesday Mar 12, 2008

Sun Tech Days, Day 1, Johannesburg, 2008

Several highlights today—presenting VisualVM during Jeff Jackson's keynote speech; participating in the "demo showcase" shootout with Sun evangelists (during which time I showed off the Swing Application Framework tooling that creates a database-accessing Swing application, ending with the always-funny crowd-pleasing Napkin look and feel); the many (very many) interactions with users; the questions; the answers; the new things I learned about NetBeans IDE. (By the way, about the "demo showcase" and the demo during Jeff Jackson's keynote—you would be surprised how much planning and stress and planning and stress goes into those. But in the end, everything went really very smoothly.)

"What," you ask. "What new things did you learn about NetBeans IDE?" Well, firstly I learned that if you want to change the font size of EVERYTHING (not just the editor font or the font of the Output window), you need to not only set "--fontsize 24" (or some other number), on Linux and Solaris, but you ALSO need to set the look and feel to something different (or only to) the MetalLookAndFeel. On Windows this is not necessary. On the other OS's, you need to set (in addition to the font size) something like "--laf javax.swing.plaf.MetalLookAndFeel". I discovered this together with one of the speakers for tomorrow. We ended up Googling for the answer (him on OpenSolaris and me on Ubuntu), because he wanted all his UI to be larger, not just fonts. And by setting the look and feel to Metal, his problem was solved, and it also worked on my Ubuntu operating system.

The second thing I learned (directly from a user) is that you can press F2 over a node or some Swing component in the editor to change its name (or, in the case of the Swing component, its "text" property). Very small tip but very useful. Just press F2 (meaning that you don't need to leave the keyboard) over something you want to rename and then you can immediately rename it.

I had so many discussions with NetBeans users (and also non-NetBeans users) that I can't remember exactly what was said anymore. One of the first conversations I had was with someone who told me that, despite his being an ardent NetBeans fan, his company had chosen IntelliJ. Why? For three reasons, of which I can only remember two. The first was that IntelliJ has better XML support. Hard to argue with that. There's great code completion and hyperlinking there. I happen to know for a fact that it wouldn't be hard to create hyperlinks in our own XML files, if we were to choose to prioritize that. I noticed in my recent sojourns into IntelliJ that you can hyperlink from a tag to its DTD or schema definition, as well as to any class declared in the XML tags. That wouldn't be hard to achieve in NetBeans IDE at all. Basically, whenever you would want to be able to link somewhere, NetBeans IDE should let you do so. The second reason they chose IntelliJ was because of TestNG support. We have at least one plugin for that, but its level of maturity seems a bit uncertain to me.

I gave out a few of our small ration of "Rich Client Programming" books. I also arranged to meet with a potential customer on Friday, after Sun Tech Days is over. I spent a lot of time at the NetBeans booth and did many small demos, a surprising numer in the NetBeans Platform area. I heavily advertized the NetBeans Platform Certified Training Course, telling people to write to users@edu.netbeans.org if they want to have a free NetBeans training course. I said that if 20 to 30 (at least) of people from this part of the world were to write asking for the course, a few of us would come from Prague (or arrange for others) to deliver the course.

So, it was a pretty good day. Tomorrow I'll be joining one of Sang Shin's hands on labs. I'll show VisualVM again and explain how (and why) to create plugins to extend VisualVM. That should be fun. I started creating an apisupport wizard that will create the stubs for a new VisualVM plugin. Not sure if I can finish it on time, but that could be pretty useful in pulling people over the threshold to developing plugins for VisualVM.

Tuesday Mar 11, 2008

NetBeans Day, Johannesburg, 2008

NetBeans Day has come and gone and it was pretty good. Brian started with the "New & Cool" session, during which time he demonstrated lots of stuff. Editor improvements, but also Ruby/Rails and Groovy/Grails support were shown! The first time Groovy/Grails was demonstrated at a NetBeans Day. The thing I learned the most from was the JavaScript support he demoed, in 6.1 Beta. He also did a great iReport demo. I also discovered that Ctrl-R (inplace rename) is MUCH better in 6.1, compared to 6.01 and 6.0. The leader of the local JUG also got some time to talk about the JUG and invite people to upcoming events, that was a pretty cool intervention.

Brian then had two others sessions. One on "Using NetBeans for Your Existing Projects", where he showed all the Ant support that the IDE has. He also started up Eclipse and showed how to convert Eclipse projects to NetBeans projects. And they weren't all simple projects; also one with a few dependencies. Next, he did a session called "Introduction to (J) Ruby on Rails". Some parts were a bit above my head, but it was good to see how cool the tooling is in the IDE. Also cool to see how easy it is to update an application with new fields. He ended with a FreeTTS integration, which ended up reciting the description of the entries he added to the web application he'd developed via Rails.

Then it was me for two hours! The first hour was on the Beans Binding Framework and the Swing Application Framework. That went much better than I had expected. The Flickr demonstration is really good, it is excellent in that it lets you explain each and every aspect of the Swing Application Framework. Both with the Beans Binding Framework and the Swing Application Framework, I used slides to explain the code and then used the Java editor in the IDE to do some coding. Only once I had shown some simple bindings, handcoded, did I move to the part where the IDE generates the bindings for you. Then I showed them the generated bindings and pointed out that the bindings were the same as the ones we had handcoded. I did a similar thing with the SimpleApplication class in the Swing Application Framework, versus the JFrame class in standard Swing. This very slow and step by step process allowed the audience to slowly atune themselves to the framework under discussion. I think most of it came across very clearly. We even ended up with a Napkin look and feel for our Flickr application at the end of it all. The application also made use of a background task and was deployed via Java Web Start. Afterwards, a couple of enthusiastic people came up to me and said they'd try out the Beans Binding Framework right away and there were some interesting questions about the Swing Application Framework.

The break followed, after which was me again on the NetBeans Platform. That was a perfect continuation from the previous session. I didn't touch on anything too complex or unexpected. Basically, I discussed the NetBeans Platform under 5 headings: "Generic Application Framework", "Infrastructural Plumbing", "Collection of Libraries", "Swing Extensions", and "NetBeans Platform Toolkit". Then I ported the Anagram Game to the NetBeans Platform and pointed the audience to the NetBeans Platform Porting Tutorial for further details.

Sang was next, the final session, on BPEL and SOA. Orchestration of web services was the main topic. I liked how he presented it and he got some engaging questions, which showed his audience was obviously very attentive.

I stupidly didn't take any pictures, even though I had a camera with me. (OK, I did take one pic, of the room before it was filled, so it just shows empty chairs, which is not that useful I guess.) It was a good day, somewhere between 200 and 250 showed up, the questions were good and there were a lot of discussions afterwards. My top three questions, i.e., those I found most interesting were: "how does the Swing Application Framework do that magical persistence handling?", "if I create a circular beans binding, what will happen?", "wouldn't it be cool if the DropDownButtonFactory were available in the palette, so I can drag and drop it?"

But, this is day one of Sun Tech Days. Tomorrow is another day and so is the next. And a lot more is set to happen during that time, I am sure.

Wednesday Mar 05, 2008

Getting Started Extending VisualVM (Part 7)

Update on 30 May 2008: The code and steps in this blog entry have been updated to reflect the VisualVM 1.0 APIs. If you want to download the complete sources of the sample discussed here, get the VisualVM Sample Collection from the Plugin Portal.

I've made some significant strides in my understanding of the VisualVM APIs. I've been following some discussions around this tool and people were asking if a single tab could be displayed to compare multiple instances of the same type of data source. Or, that's what they meant, in API terms. So as a prototype, I made two new tabs on "Local Host" level, showing the system properties and JVM arguments of ALL the running instances at the same time:

Above you see the system properties of all the running Java applications on my local system, while below you see all their JVM arguments:

That's pretty cool, I reckon. One really nice thing is that you can also see the icons in the tabs above. Those icons match the icons of the application in the explorer view. Really useful.

Something I had to do—less than ideal but it works—was to copy the JVMArgumentsViewSupport and the SystemPropertiesViewSupport from the sources. I'm hoping those two will become public when the APIs are officially released. Until then, this solution works.

Here's a screenshot of all the classes that went into the creation of the above two tabs:

It ended up being relatively simple. My key learnings were the following:

  • A reaffirmation that I don't need to create separate menu items for the Open action. Simply return "true" from DataSourceViewProvider.supportsViewFor and then you will automatically have your view open together with the other views opened from the related node in the explorer view.

  • There are some very handy classes for getting things, such as properties, from the JVM (or should that now be "VM"):

    • com.sun.tools.visualvm.core.model.jvm.JVM
    • com.sun.tools.visualvm.core.model.jvm.JVMFactory

    The final snippet below shows how the above two are used.

  • In the SystemPropertiesViewSupport and JVMArgumentsViewSupport, I created the icon like this, using the final argument of DataViewComponent.DetailsView to return the label with the icon, which is then put into the tabs that you see above:

    private Application app;
    private int count;
    private String name;
    
    public SystemPropertiesViewSupport(Properties properties, Application app, 
            int count, String name) {
        initComponents(properties);
        this.app = app;
        this.count = count;
        this.name = name;
    }
    String cleanedUserDir = "";
    
    public DataViewComponent.DetailsView getDetailsView() {
        Image icon = DataSourceDescriptorFactory.getDescriptor(app).getIcon();
        JLabel label = wrap(icon);
        JComponent[] options = {label};
        String title = count + ": " + name + " (pid " + app.getPid() + ")";
        return new DataViewComponent.DetailsView(title, null, this, options);
    }
    
    private JLabel wrap(Image image) {
        ImageIcon icon = new ImageIcon(image);
        JLabel label = new JLabel(icon, JLabel.CENTER);
        return label;
    }
    ...
    ...
    ...

Finally, for what it's worth, both DataViewComponents in my DataSourceViews look like this:

private DataViewComponent createViewComponent() {

    //Data area for master view:
    JEditorPane generalDataArea = new JEditorPane();
    generalDataArea.setText("Below you see the system properties of" +
            " all running apps!");
    generalDataArea.setBorder(BorderFactory.createEmptyBorder(14, 8, 14, 8));

    //Master view:
    DataViewComponent.MasterView masterView =
            new DataViewComponent.MasterView("All System Properties",
            null, generalDataArea);

    //Configuration of master view:
    DataViewComponent.MasterViewConfiguration masterConfiguration =
            new DataViewComponent.MasterViewConfiguration(false);

    //Add the master view and configuration view to the component:
    dvc = new DataViewComponent(masterView, masterConfiguration);

    //Get all the applications deployed to the host:
    Set apps = host.getApplications();

    //Get the iterator:
    Iterator it = apps.iterator();

    //Set count to zero:
    int count = 0;

    //Iterate through our applications:
    while (it.hasNext()) {

        //Increase the count:
        count = count + 1;

        //Now we have our application:
        Application app = it.next();

        //Get the process id:
        String pid = count + ": " + (String.valueOf(app.getPid()));

        //Get the system properties:
        Properties jvmProperties = null;
        JVM jvm = JVMFactory.getJVMFor(app);
        if (jvm.isGetSystemPropertiesSupported()) {
            jvmProperties = jvm.getSystemProperties();
        }

        //Extrapolate the name from the type:
        ApplicationType appType = ApplicationTypeFactory.
                getApplicationTypeFor(app);
        String appName = appType.getName();

        //Put the first application top left:
        if (count == 1) {
            dvc.addDetailsView(new SystemPropertiesViewSupport(
                    jvmProperties, app, count, appName).getDetailsView(), 
                    DataViewComponent.TOP_LEFT);
            dvc.configureDetailsArea(new DataViewComponent.DetailsAreaConfiguration(
                    pid, true), DataViewComponent.TOP_LEFT);

        //Put the second application top right:
        } else if (count == 2) {
            dvc.addDetailsView(new SystemPropertiesViewSupport(
                    jvmProperties, app, count, appName).getDetailsView(), 
                    DataViewComponent.TOP_RIGHT);
            dvc.configureDetailsArea(new DataViewComponent.DetailsAreaConfiguration(
                    pid, true), DataViewComponent.TOP_RIGHT);

        //Put the third application bottom left:    
        } else if (count == 3) {
            dvc.addDetailsView(new SystemPropertiesViewSupport(
                    jvmProperties, app, count, appName).getDetailsView(), 
                    DataViewComponent.BOTTOM_LEFT);
            dvc.configureDetailsArea(new DataViewComponent.DetailsAreaConfiguration(
                    pid, true), DataViewComponent.BOTTOM_LEFT);

        //Put the fourth application bottom right:        
        } else if (count == 4) {
            dvc.addDetailsView(new SystemPropertiesViewSupport(
                    jvmProperties, app, count, appName).getDetailsView(), 
                    DataViewComponent.BOTTOM_RIGHT);
            dvc.configureDetailsArea(new DataViewComponent.DetailsAreaConfiguration(
                    pid, true), DataViewComponent.BOTTOM_RIGHT);

        //Put all other applications bottom right, 
        //which creates tabs within the bottom right tab    
        } else {
            dvc.addDetailsView(new SystemPropertiesViewSupport(
                    jvmProperties, app, count, appName).getDetailsView(), 
                    DataViewComponent.BOTTOM_RIGHT);
            dvc.configureDetailsArea(new DataViewComponent.DetailsAreaConfiguration(
                    pid, true), DataViewComponent.BOTTOM_RIGHT);
        }

    }

    return dvc;

}

And here's the result again, this time with org.openide.awt.DropDownButtonFactory:

Update on 28 May 2008: The code and steps in this blog entry have been updated to reflect the VisualVM 1.0 APIs. If you want to download the complete sources of the sample discussed here, get the VisualVM Sample Collection from the Plugin Portal.

About

Geertjan Wielenga (@geertjanw) is a Principal Product Manager in the Oracle Developer Tools group living & working in Amsterdam. He is a Java technology enthusiast, evangelist, trainer, speaker, and writer. He blogs here daily.

The focus of this blog is mostly on NetBeans (a development tool primarily for Java programmers), with an occasional reference to NetBeans, and sometimes diverging to topics relating to NetBeans. And then there are days when NetBeans is mentioned, just for a change.

Search

Archives
« March 2008 »
SunMonTueWedThuFriSat
      
6
7
8
9
10
16
19
20
21
22
26
     
Today