Wednesday Apr 09, 2008

Plugin for Creating Web Framework Plugins

Let's say you're beginning to create a plugin that will integrate tools for some web framework into NetBeans IDE. Download this plugin to help you get started:

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

Install it in NetBeans IDE 6.1 Beta:

  1. Create a new module called, for example, FooFrameworkSupport.

  2. Right-click it and choose "Web Framework Provider Support".

  3. In the first panel, choose a library containing the JARs required by your web framework. You can also add those JARs to a new library, after clicking "Manage Libraries". Finally, make sure to check the "Use in New Web Framework" checkbox! Click Next.

  4. In the second panel, change the suggested library name and display name, if needed. Click Finish and wait a bit for the files to be generated.

  5. Inspect the generated sources. You get a WebFrameworkProvider class, a WebModuleExtender class, and an empty configuration panel. You also get a library descriptor. All of these are correctly registered in the layer.xml file and the necessary API dependencies are in the project.xml file.

  6. Install the module right away without making any changes to the code at all. Then create a new web application and notice, in the final panel, i.e., the Frameworks panel, that a new entry is available for your web framework. The configuration panel is empty. Select the checkbox next to the name of the web framework, click Finish, and the library is added to the application's classpath.

Now study the NetBeans sources, such as web.struts, to see how to continue developing the plugin. You probably want to let the user configure something in the Frameworks panel so that some artifacts specific to the framework are generated into the web application source structure when the wizard is completed. The next version of this plugin will add some code that will help you get started with this part.

Tuesday Apr 08, 2008

New & Updated Samples for VisualVM Plugin Developers

There was a big repackaging over the past weeks, for the VisualVM APIs. However, two examples are available, right now, that you can look at when migrating some plugin that you may have created previously. If you check out the sources from https://visualvm.dev.java.net/, you will find a 'samples' folder, with two samples, as you can see here:

Those are two standalone plugins for VisualVM. Just open them in the IDE, set the latest VisualVM as your platform (you probably need to create your own version of VisualVM from the sources you've checked out, by choosing "Build ZIP Distribution" on the VisualVM application in the IDE and then unzipping the generated ZIP that you'll find in the 'dist' folder). Then everything should be fine, no error marks and so on, as you can see here:

The "Hello World" sample does the same as before, except that it is now updated to the latest APIs:

The second sample, providing the "JVM capabilities" subtab that you see in the bottom right of the screenshot below, shows how a plugin can integrate into an existing tab using the new API and save/restore its data to/from Application Snapshot:

So, if you're interested in integrating your tools into VisualVM, the above are the two samples you should look at. Both will find their way into a tutorial soon.

Monday Apr 07, 2008

Great Spring Support in IDE

I'm pretty impressed by the Spring support in the NetBeans IDE 6.1 development builds. Among other things, you can jump from references to classes and methods to their definitions in Java classes:

But the best thing is... that all the properties in a referenced class are available in the code completion box, within the context of the bean that references the class:

That's definitely my favorite Spring feature in the IDE. So, if you go to the "com.sun.swingset3.SwingSet3" class, which is the referenced class in the screenshot above, you will find methods named "setCurrentDemo", etc. Even better, you can jump via a hyperlink from the above reference to the definition of the setter in the Java class. Very cool and extremely handy. Basically, everything that can be referenced from the Java side is available via code completion and hyperlinks in the Spring configuration file.

Another cool thing is that, since you're dealing with an XML file, you have the Navigator to show you the complete breakdown of your Spring configuration file:

The best enhancement I can imagine would be... a visual view. Some kind of designer that gives you a graphic view on top of the XML tags...

Sunday Apr 06, 2008

Disabling/Enabling a Wizard Panel's Finish Button

Let's take the wizard panel from a few days ago and disable the "Finish" button until both the new fields are filled in:

  1. Change the visual panel's signature so that it implements DocumentListener. Then, in the constructor, add a DocumentListener to both of your new fields:

    extField.getDocument().addDocumentListener(this);
    impField.getDocument().addDocumentListener(this);

    Next, implement the DocumentListener's methods like this:

    @Override
    public void changedUpdate(DocumentEvent e) {
        panel.fireChangeEvent();
        updateUI(e);
    }
    
    @Override
    public void insertUpdate(DocumentEvent e) {
        panel.fireChangeEvent();
        updateUI(e);
    }
    
    @Override
    public void removeUpdate(DocumentEvent e) {
        panel.fireChangeEvent();
        updateUI(e);
    }
    
    public void updateUI(DocumentEvent e) {
        if (this.extField.getDocument() == e.getDocument()) {
            firePropertyChange(EXTENSION, null, this.extField.getText());
        }
        if (this.impField.getDocument() == e.getDocument()) {
            firePropertyChange(IMPLEMENTATION, null, this.impField.getText());
        }
    }

  2. In the non-visual wizard panel, add the code that receives the change event and enables/disables the "Finish" button:

    private final Set<ChangeListener> listeners = new HashSet<ChangeListener>(1);
    
    @Override
    public final void addChangeListener(ChangeListener l) {
        synchronized (listeners) {
            listeners.add(l);
        }
    }
    
    @Override
    public final void removeChangeListener(ChangeListener l) {
        synchronized (listeners) {
            listeners.remove(l);
        }
    }
    
    protected final void fireChangeEvent() {
        Set ls;
        synchronized (listeners) {
            ls = new HashSet(listeners);
        }
        ChangeEvent ev = new ChangeEvent(this);
        for (ChangeListener l : ls) {
            l.stateChanged(ev);
        }
    }
    
    @Override
    public boolean isValid() {
        if (getExtensionFromVisualPanel().length() == 0 || 
                getImplementationFromVisualPanel().length() == 0) {
            return false;
        }
        return true;
    }

  3. Finally, make sure the two classes are correctly hooked up together. In the wizard panel, pass the current class to the visual panel:

    @Override
    public Component getComponent() {
        if (component == null) {
            component = new FancyVisualPanel1(this);
        }
        return component;
    }

    In the visual panel, receive the wizard panel in the constructor:

    private FancyWizardPanel1 panel;
    
    public FancyVisualPanel1(FancyWizardPanel1 panel) {
        initComponents();
        this.panel = panel;
        extField.getDocument().addDocumentListener(this);
        impField.getDocument().addDocumentListener(this);
    }

That's it. Now, if both fields aren't filled in, the "Finish" button is disabled. You can also display some kind of informative message so that the user knows what they should do to enable the "Finish" button. In real life, you probably don't want to force the user to fill in an extension class and an implementation class, because possibly they want to have a signature that doesn't include superclasses and/or interfaces. So you'd probably add a checkbox that enables/disables the two fields, depending on whether the user wants to fill them in or not.

Thursday Apr 03, 2008

John O'Conner: Best Technical Writer in the World?

I am a big fan of John O'Conner's writing. He has a blog on java.net and he also has a number of articles in other places. Below follow 3 reasons why I believe he is "the best technical writer in the world":

  1. Very detailed technical knowledge, coupled with great didactic skills, and a wonderful clarity with words. Two articles I'd like to mention as evidence here are these:

    These two articles I have personally found extremely useful. In the first case, he's the first person to have written an article that uses an actual, downloadable, example to contrast the JDK's java.util.ServiceLoader class against the NetBeans API org.openide.util.Lookup class. Currently, I believe that that article is (by far) the best place to begin when you're learning about the NetBeans Lookup class (even though the code is outdated, specifically, it is relevant for 5.5, though it still works in 6.0+, although from 6.0 onwards, the Lookup code has been simplified).

    The second article is excellent too: first, you find out, by means of actual code, what life is like WITHOUT beans binding. Then, he goes into detail about the beans binding framework, what it can give you, again using fully compilable code throughout.

    On both these topics, I'd recommend the above two documents before any other. They ooze the air of having been written FOR programmers BY a programmer, which is not always the case.

  2. Concern for the customer and a common sense approach to stuff in general. He has some great blog entries where he shares conversations he's had with people about some technology or other:

    I like the approach taken in those blog entries very much.

  3. Honesty. Even though he likes NetBeans IDE (another big plus in my book) he is honest about its failings when he finds them, such as in his recent blog entry NetBeans 6.1, UTF-8 encoded source files, and a tale of corruption.

On top of that, he has big name recognition and trust in the developer community, which you can see by the fact that he gets LOTS of comments to his blog entries. So, here's to John O'Conner!

Another Edition of the NetBeans Platform Certified Training

Last week, we held a NetBeans Platform Certified Training session, over Thursday and Friday, here in Prague:

It was a pretty good event. It was the first time I took my new hint template on the road for real. It worked and some of the students seem to have already been using it, judging from some of the comments to the mailing list we created to give them direct access to us. We covered all the typical NetBeans Platform subjects, from Lookup to the NetBeans window system to the NetBeans FileSystem, nodes, data objects, and so on. The students now need to do some work and turn in their assignments, which we will judge and give them feedback on.

Here's the page that's been set up especially for this particular course. If you'd like to let us know that we should come and visit your school/institution/club/company/JUG/city/pub to deliver this course, drop a line at users@edu.netbeans.org.

Today on Javalobby. Porting a Hot Java Thread Detector to VisualVM.

Tuesday Apr 01, 2008

Creating a Better Java Class Wizard

At the end of my recent Javalobby article, Overview of NetBeans Java API Samples in Plugin Portal, two users asked for a more advanced wizard for Java classes, one that would let the user specify superclasses and interfaces right in the wizard. Fine. Let's create exactly that. If you work through everything in this blog entry, you will end up with a new template in the New File dialog:

When you click Next, you'll get everything as usual, except also the possibility to fill in superclasses and interfaces (all on the same panel, hurray):

On clicking Finish, you'll have a new class, with the superclasses and interfaces you specified generated automatically right into the class, and lightbulbs to click through to generate the import statements and required methods:

If you need this, or something like that, follow along below:

  1. Create a new module, called "FancyJavaClassTemplate", with code name base "org.netbeans.modules.fancy".

  2. In the New File dialog, use the Wizard wizard to create stubs for a new wizard, with "New File" under "Registration Type" and "1" in "Number of Wizard Panels":

  3. Type values in the final panel and click Finish. Then look at the generated sources. First look at the layer.xml file, then at the Java classes, and then at nbproject/project.xml.

  4. Use FancyVisualPanel1 to design the extra panel that you want to show, making sure that you call the first field "extField" and the second "impField" (because the code later on in this blog entry will assume that you've done so):

    Don't worry about the top part of what you see in the second screenshot above, because that's simply reused from the NetBeans internals, as you will see.

  5. Set dependencies on 'Datasystems API', 'Filesystem API', 'Java project Support', 'Nodes API', 'Project API', and 'Project UI API'.

  6. Open the class called "FancyWizardIterator.java". Rewrite the start of the getPanels() method as follows and make sure to also declare the packageChooserPanel, as shown below:

    private WizardDescriptor.Panel packageChooserPanel;
    
    private WizardDescriptor.Panel[] getPanels() {
        Project project = Templates.getProject(wizard);
        Sources sources = (Sources) project.getLookup().lookup(Sources.class);
        SourceGroup[] groups = sources.getSourceGroups(JavaProjectConstants.SOURCES_TYPE_JAVA);
        packageChooserPanel = JavaTemplates.createPackageChooser(project, groups, new FancyWizardPanel1());
        if (panels == null) {
            panels = new WizardDescriptor.Panel[]{
                        packageChooserPanel,
                    };
            String[] steps = createSteps();
            ...
            ...
            ...

    Fix imports and make sure that you have ALL of these as your import statements:

    import java.awt.Component;
    import java.io.IOException;
    import java.util.Collections;
    import java.util.NoSuchElementException;
    import java.util.Set;
    import javax.swing.JComponent;
    import javax.swing.event.ChangeListener;
    import org.netbeans.api.java.project.JavaProjectConstants;
    import org.netbeans.api.project.Project;
    import org.netbeans.api.project.SourceGroup;
    import org.netbeans.api.project.Sources;
    import org.netbeans.spi.project.ui.templates.support.Templates;
    import org.openide.WizardDescriptor;

  7. Install and see the results. You should see the panels shown at the start of this blog entry. But nothing will happen when you click Finish, yet. Probably the icon will be different. (You can set that by copying from another template, if you expand "this layer in context" in the layer.xml, then find another template that has the icon you want and choose "Open Layer File(s)". Then copy the attribute that points to the icon.)

  8. Go to the Template Manager under the Tools menu and open Classes | Java Class into the editor. You should see this:

    <#assign licenseFirst = "/\*">
    <#assign licensePrefix = " \* ">
    <#assign licenseLast = " \*/">
    <#include "../Licenses/license-${project.license}.txt">
    
    <#if package?? && package != "">
    package ${package};
    
    </#if>
    /\*\*
     \*
     \* @author ${user}
     \*/
    public class ${name} {
    
    }

    Create a new empty file, call it "Fancy.ftl", and then copy all of the above into it. Then add the bit in bold below:

    <#assign licenseFirst = "/\*">
    <#assign licensePrefix = " \* ">
    <#assign licenseLast = " \*/">
    <#include "../Licenses/license-${project.license}.txt">
    
    <#if package?? && package != "">
    package ${package};
    
    </#if>
    /\*\*
     \*
     \* @author ${user}
     \*/
    public class ${name} ${extends} ${implements} {
    
    }

  9. Open the layer.xml file and tweak it so that it has the same content as the below. Pay particular attention to the bits that are in bold and make VERY sure that your layer.xml file has these too after you've finished tweaking it:

    <filesystem>
        <folder name="Templates">
            <folder name="Classes">
                <file name="fancy.java" url="Fancy.ftl">
                    <attr name="SystemFileSystem.localizingBundle" stringvalue="org.netbeans.modules.fancy.Bundle"/>
                    <attr name="instantiatingIterator" newvalue="org.netbeans.modules.fancy.FancyWizardIterator"/>
                    <attr name="template" boolvalue="true"/>
                    <attr name="SystemFileSystem.icon" urlvalue="nbresloc:org/netbeans/modules/java/resources/class.gif"/>
                    <attr name="templateWizardURL" urlvalue="nbresloc:/org/netbeans/modules/fancy/fancy.html"/>
                    <attr name="javax.script.ScriptEngine" stringvalue="freemarker"/>
                </file>
            </folder>
        </folder>
    </filesystem>

    In other words, make sure that the first line in bold above has ".java" as the name's extension and that "Fancy.ftl" is set as the URL. Check to make sure that your template has exactly that name. It is case sensitive, so be careful. The second line in bold above is needed because that activates the FreeMarker scripting engine, which is what you need to have the NetBeans Platform do for you in order for the template to be generated correctly.

  10. Now we will pass the values from the panel into the template above. Firstly, in the visual panel, make the values in the text fields accessible:

    public String getExtension() {
        return extField.getText();
    }
    
    public String getImplementation() {
        return impField.getText();
    }

    Also add two fields to the top of the visual panel class:

    static final String EXTENSION = "extension";
    static final String IMPLEMENTATION = "implementation";

    In the wizard panel (i.e., NOT the visual panel), retrieve the above values:

    private String getExtensionFromVisualPanel() {
        return ((FancyVisualPanel1) component).getExtension();
    }
    
    private String getImplementationFromVisualPanel() {
        return ((FancyVisualPanel1) component).getImplementation();
    }

    Store the value by overriding the store method in the wizard panel:

    @Override
    public void storeSettings(Object settings) {
        ((WizardDescriptor) settings).putProperty(FancyVisualPanel1.EXTENSION,  getExtensionFromVisualPanel());
        ((WizardDescriptor) settings).putProperty(FancyVisualPanel1.IMPLEMENTATION,  getImplementationFromVisualPanel());
    }

  11. To wrap it all up together, implement the iterator's instantiate() method as follows:

    public Set instantiate() throws IOException {
    
        //Prepare the arguments for passing to the FreeMarker template:
        String extension = (String) wizard.getProperty(FancyVisualPanel1.EXTENSION);
        String implementation = (String) wizard.getProperty(FancyVisualPanel1.IMPLEMENTATION);
        Map args = new HashMap();
        args.put("extends", "extends " + extension);
        args.put("implements", "implements " + implementation);
    
        //Get the template and convert it:
        FileObject template = Templates.getTemplate(wizard);
        DataObject dTemplate = DataObject.find(template);
    
        //Get the package:
        FileObject dir = Templates.getTargetFolder(wizard);
        DataFolder df = DataFolder.findFolder(dir);
    
        //Get the class:
        String targetName = Templates.getTargetName(wizard);
    
        //Define the template from the above,
        //passing the package, the file name, and the map of strings to the template:
        DataObject dobj = dTemplate.createFromTemplate(df, targetName, args);
    
        //Obtain a FileObject:
        FileObject createdFile = dobj.getPrimaryFile();
    
        //Create the new file:
        return Collections.singleton(createdFile);
    
    }

  12. You're finished! Install the plugin and now the template should create the file for you.

Homework assignments—figure out what should happen when the user types NOTHING in one of the new fields in the wizard. Should you do something in the code to catch that? The wizards can provide validation functionality, which you would need to enable, causing red error messages to appear in the panel under specified conditions, such as the fields not being (correctly) filled out. Or... should you make use of the FreeMarker template to add logic to it there so that if an empty string is passed, nothing is generated? Also, figure out how to HIDE the existing Java class wizard and let your own wizard replace it.

Further resources—FreeMarker Template Sample in the Plugin Portal, which gives you additional samples for wizards just like this (including one for generating an HTML file), as well as syntax coloring and code completion for FreeMarker templates.

Today on NetBeans Zone. How Fast Can OpenOffice.org Be Extended?

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
« April 2008 »
SunMonTueWedThuFriSat
  
2
4
5
13
18
19
23
30
   
       
Today