Thursday Jul 31, 2008

Groovy: Bridge Between Eclipse and NetBeans IDE?

If you're able to use a Groovy script to generate a NetBeans Platform application (as shown over the past few days in this blog), then why not go a step further and also generate two additional files—".project" and ".classpath", as shown below...

...because those two files happen to be all that's needed for an application to be an Eclipse project. You can then simply open the NetBeans Platform application right inside Eclipse:

And then you can use NetBeans IDE to generate various NetBeans API implementations, since Eclipse doesn't have the cool wizards that NetBeans IDE makes available for this purpose. After that, simply continue using Eclipse, if you're more comfortable with that editor or if one/more of your team mates use Eclipse while you prefer NetBeans.

By the way, I really like how an Eclispe project's classpath is declared in the above shown external ".classpath" file, because that means that I'm able to include as many NetBeans Platform JARs as I want in the process that generates the file. I.e., when the Eclipse project is opened in Eclipse, all the NetBeans Platform JARs (or as many/few as are set in the Groovy script) are on the Eclipse project's classpath.

Previously, I'd need Maven to achieve the result above. Now, via Groovy, I can generate an IDE-polyglot project in an easily customizable style and language. Thanks, Groovy, you're groovy.

Tuesday Jul 29, 2008

World's Simplest GroovyBuilder

Here's a GroovyBuilder for generating NetBeans projects such as the ones I showed in the past days:
package generators

import groovy.util.BuilderSupport

class NBProjectGenerator extends BuilderSupport {

    protected void setParent(Object parent, Object child) {
    }

    protected Object createNode(Object name) {
        return null
    }

    //Here we receive the path to a folder that should be created:
    protected Object createNode(Object name, Object value) {
        def folder = new File(value)
        folder.mkdirs()
    }

    //Here we create a new file in a specified location
    //and with a specified content:
    protected Object createNode(Object name, Map attrs) {
        new File(attrs.get("loc") + File.separator + attrs.get("name")).write(attrs.get("file"))
    }

    protected Object createNode(Object name, Map attrs, Object value) {
        return null
    }
    
}

It could be used for generating any kind of source structure, not only NetBeans projects at all, with folders and files being the two only items of concern. This is how it would be used to create a new NetBeans module:

NBProjectGenerator nbProjectGenerator = new NBProjectGenerator()
nbProjectGenerator.generate() {
    //Root folder:
    folderProp(folder)
    //Source folder:
    srcProp(src)
    //NetBeans project folder:
    nbprojectProp(nbproject)
    //Main package:
    pkgsProp(pkgs)
    //Ant build.xml script:
    buildXmlProp(loc:folder, file:build_xml, name:"build.xml")
    //Build-impl.xml script:
    buildImplXmlProp(loc:nbproject, file:build_impl_xml, name:"build-impl.xml")
    //Bundle.properties:
    bundleProp(loc:pkgs, file:bundle_properties, name:"Bundle.properties")
    //Layer file:
    layerXmlProp(loc:pkgs, file:layer_xml, name:"layer.xml")
    //Manifest file:
    manifestMfProp(loc:folder, file:manifest_mf, name:"manifest.mf")
    //Platform properties:
    platformProp(loc:nbproject, file:platform_properties, name:"platform.properties")
    //Project properties:
    projectProp(loc:nbproject, file:project_properties, name:"project.properties")
    //Project XML file:
    projectXmlProp(loc:nbproject, file:project_xml, name:"project.xml")
}

Using the very same GroovyBuilder, you can generate a NetBeans Platform application. Only remember to exclude several of the statements above, since a NetBeans Platform application doesn't have its own build.xml, for example:

NBProjectGenerator nbProjectGenerator = new NBProjectGenerator()
nbProjectGenerator.generate() {
    //Root folder:
    folderProp(folder)
    //NetBeans project folder:
    nbprojectProp(nbproject)
    //Platform properties:
    platformProp(loc:nbproject, file:platform_properties, name:"platform.properties")
    //Project properties:
    projectProp(loc:nbproject, file:project_properties, name:"project.properties")
    //Project XML file:
    projectXmlProp(loc:nbproject, file:project_xml, name:"project.xml")
}

Something similar could be done for NetBeans web applications too, for example. The only thing that needs to be done is for the values to be retrieved somehow. Right now I have them hardcoded in my Groovy script (as shown in the blog entries of the last two days). However, I could access a file on disk via Groovy and set that as the value that is passed to the GroovyBuilder. And I could create another GroovyBuilder to simplify that in the same way as the above. I'd then be accessing two GroovyBuilders from within the same Groovy script and then generating NetBeans projects very quickly, efficiently, and transparently. The Groovy script itself would end up being pretty short and with no superfluous, repetitive boilerplate code, because that's moved to the GroovyBuilder class. All that's pretty neat!

Monday Jul 28, 2008

Groovy Generation of NetBeans Modules

I'm continuing to be impressed with the usefulness of Groovy in getting started with source structures for applications. Yesterday I showed how to generate a NetBeans Platform application from a Groovy script. Today I present... a Groovy script that will generate a NetBeans module. This is pretty much the absolutely barest NetBeans module imaginable. Nonetheless, a NetBeans module is what it is, and no different from the one you get via the "New Module" wizard in the "New Project" dialog. And once you've run this script, you'll be able to open the module in the IDE and begin adding features to it right away.
package generators

def id = new Date().time
def folder = System.getProperty("user.home") + "/NewModule" + id;
def name = "NewModule" + id;
def pkgs = "org/netbeans/modules/demo"

println "Generating project ${name} in folder ${folder}";

def manifest_mf = """\\
Manifest-Version: 1.0
OpenIDE-Module: ${name}
OpenIDE-Module-Layer: ${pkgs}/layer.xml
OpenIDE-Module-Localizing-Bundle: ${pkgs}/Bundle.properties
OpenIDE-Module-Specification-Version: 1.0

"""

def build_xml = """\\
<?xml version="1.0" encoding="UTF-8"?>
<!-- You may freely edit this file. See harness/README in the NetBeans platform -->
<!-- for some information on what you could do (e.g. targets to override). -->
<!-- If you delete this file and reopen the project it will be recreated. -->
<project name="${name}" basedir=".">
    <description>Builds, tests, and runs the project ${name}.</description>
    <import file="nbproject/build-impl.xml"/>
</project>

"""

def layer_xml = """\\
<?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>
</filesystem>

"""

def bundle_properties = """\\
OpenIDE-Module-Name=${name}

"""

def build_impl_xml = """\\
<?xml version="1.0" encoding="UTF-8"?>
<!--
\*\*\* GENERATED FROM project.xml - DO NOT EDIT  \*\*\*
\*\*\*         EDIT ../build.xml INSTEAD         \*\*\*
-->
<project name="${name}-impl" basedir="..">
    <property file="nbproject/private/platform-private.properties"/>
    <property file="nbproject/platform.properties"/>
    <macrodef name="property" uri="http://www.netbeans.org/ns/nb-module-project/2">
        <attribute name="name"/>
        <attribute name="value"/>
        <sequential>
            <property name="@{name}" value="\\${@{value}}"/>
        </sequential>
    </macrodef>
    <property file="\\${user.properties.file}"/>
    <nbmproject2:property name="harness.dir" value="nbplatform.\\${nbplatform.active}.harness.dir" xmlns:nbmproject2="http://www.netbeans.org/ns/nb-module-project/2"/>
    <nbmproject2:property name="netbeans.dest.dir" value="nbplatform.\\${nbplatform.active}.netbeans.dest.dir" xmlns:nbmproject2="http://www.netbeans.org/ns/nb-module-project/2"/>
    <fail message="You must define 'nbplatform.\\${nbplatform.active}.harness.dir'">
        <condition>
            <not>
                <available file="\\${harness.dir}" type="dir"/>
            </not>
        </condition>
    </fail>
    <import file="\\${harness.dir}/build.xml"/>
</project>

"""
def platform_properties = """\\
nbplatform.active=default

"""
def project_properties = """\\
javac.source=1.5
javac.compilerargs=-Xlint -Xlint:-serial

"""
def project_xml = """\\
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://www.netbeans.org/ns/project/1">
    <type>org.netbeans.modules.apisupport.project</type>
    <configuration>
        <data xmlns="http://www.netbeans.org/ns/nb-module-project/3">
            <code-name-base>${name}</code-name-base>
            <standalone/>
            <module-dependencies/>
            <public-packages/>
        </data>
    </configuration>
</project>

"""
def base = new File(folder);
base.mkdirs();
def src = new File(folder + File.separator + "src" + File.separator + "${pkgs}");
src.mkdirs();
def nbproject = new File(folder + File.separator + "nbproject");
nbproject.mkdirs();
new File(folder + File.separator  +"build.xml").write(build_xml);
new File(folder + File.separator  +"manifest.mf").write(manifest_mf);
new File("${src}" + File.separator + "layer.xml").write(layer_xml);
new File("${src}" + File.separator + "Bundle.properties").write(bundle_properties);
new File("${nbproject}" + File.separator + "build-impl.xml").write(build_impl_xml);
new File("${nbproject}" + File.separator + "platform.properties").write(platform_properties);
new File("${nbproject}" + File.separator + "project.properties").write(project_properties);
new File("${nbproject}" + File.separator + "project.xml").write(project_xml);

println("Done.")

The screenshot below has two nodes highlighted. The first is the node of the Groovy script that I ran to generate the whole of the source structure represented by the second highlighted node:

I.e., running the above script gives you a compilable starting point for creating NetBeans modules, without needing to go through any wizards to do so. The script runs in the space of a split second, generating everything I need. Plus, it's very simple to tweak the script to make it generate something different. Imagine, for example, how easy it would be to have a few predefined actions or windows in your module? Just go through the motions using the IDE's tools and then copy the generated code into the script above and then you'll never need to create them again via wizards, but always via your script instead.

Sunday Jul 27, 2008

Groovy Generation of Modular Desktop Applications

I came across a rather cool blog entry today (via the always excellent http://groovyblogs.org/), ICEFaces Project Generation Using Groovy, by Rob Mayhew. Very cool that he included all his code. A few tweaks later I was able to generate a new NetBeans Platform application from Groovy:
package generators

def folder = "/home/geertjan/Desktop/wonderful";
def name = "MyWonderfulApplication";

println "Generating project ${name} in folder ${folder}";

def build = """\\
<?xml version="1.0" encoding="UTF-8"?>
<!-- You may freely edit this file. See harness/README in the NetBeans platform -->
<!-- for some information on what you could do (e.g. targets to override). -->
<!-- If you delete this file and reopen the project it will be recreated. -->
<project name="${name}" basedir=".">
    <description>Builds the module suite ${name}.</description>
    <import file="nbproject/build-impl.xml"/>
</project>

"""
def build_impl_xml = """\\
<?xml version="1.0" encoding="UTF-8"?>
<!--
\*\*\* GENERATED FROM project.xml - DO NOT EDIT  \*\*\*
\*\*\*         EDIT ../build.xml INSTEAD         \*\*\*
-->
<project name="${name}-impl" basedir=".." xmlns:sproject="http://www.netbeans.org/ns/nb-module-suite-project/1">
    <property file="nbproject/private/platform-private.properties"/>
    <property file="nbproject/platform.properties"/>
    <macrodef name="property" uri="http://www.netbeans.org/ns/nb-module-suite-project/1">
        <attribute name="name"/>
        <attribute name="value"/>
        <sequential>
            <property name="@{name}" value="\\${@{value}}"/>
        </sequential>
    </macrodef>
    <property file="\\${user.properties.file}"/>
    <sproject:property name="harness.dir" value="nbplatform.\\${nbplatform.active}.harness.dir"/>
    <sproject:property name="netbeans.dest.dir" value="nbplatform.\\${nbplatform.active}.netbeans.dest.dir"/>
    <fail message="You must define 'nbplatform.\\${nbplatform.active}.harness.dir'">
        <condition>
            <not>
                <available file="\\${harness.dir}" type="dir"/>
            </not>
        </condition>
    </fail>
    <import file="\\${harness.dir}/suite.xml"/>
</project>

"""
def platform_properties = """\\
disabled.clusters=\\
    apisupport1,\\
    enterprise5,\\
    groovy1,\\
    gsf1,\\
    harness,\\
    ide10,\\
    java2,\\
    nb6.5,\\
    profiler3,\\
    visualweb2,\\
    webcommon1,\\
    websvccommon1,\\
    xml2
disabled.modules=\\
    org.netbeans.libs.jsr223,\\
    org.openide.compat,\\
    org.netbeans.modules.autoupdate.services,\\
    org.netbeans.api.visual,\\
    org.netbeans.core.execution,\\
    org.netbeans.core.multiview,\\
    org.openide.execution,\\
    org.openide.options,\\
    org.netbeans.modules.favorites,\\
    org.netbeans.modules.templates,\\
    org.netbeans.modules.autoupdate.ui,\\
    org.openide.util.enumerations,\\
    org.netbeans.modules.core.kit
enabled.clusters=\\
    platform9
nbplatform.active=default

"""
def project_properties = """\\

app.name=token
app.title=${name}
branding.token=\\${app.name}
modules=

"""
def project_xml = """\\
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://www.netbeans.org/ns/project/1">
    <type>org.netbeans.modules.apisupport.project.suite</type>
    <configuration>
        <data xmlns="http://www.netbeans.org/ns/nb-module-suite-project/1">
            <name>${name}</name>
        </data>
    </configuration>
</project>

"""
def base = new File(folder);
base.mkdirs();
def nbproject = new File(folder + File.separator + "nbproject");
nbproject.mkdirs();
new File(folder + File.separator  +"build.xml").write(build);
new File(folder + File.separator + "nbproject" + File.separator + "build-impl.xml").write(build_impl_xml);
new File(folder + File.separator + "nbproject" + File.separator + "platform.properties").write(platform_properties);
new File(folder + File.separator + "nbproject" + File.separator + "project.properties").write(project_properties);
new File(folder + File.separator + "nbproject" + File.separator + "project.xml").write(project_xml);

println("Done.")

Rather wonderfully, running the above script will give you a NetBeans project which, when you open it, will give you this:

That's a NetBeans Platform application which, when deployed, gives you this:

Now... imagine that you were to create different Groovy scripts, each providing different starting points for developing an application on the NetBeans Platform. One Groovy script would provide the application above, i.e., the same as you get via the NetBeans Platform Application wizard in the New Project dialog. Another one, however, could include a few basic modules that provide a more fleshed out layout. Or maybe you'd like to have the Plugin Manager available by default? Simply tweak the script to include the applicable modules, which you could simulate via the ui in the IDE and then copy the generated tags over to your Groovy script. In the end, you'd never need to go through a template in the IDE again, because you could, in the blink of an eye, generate everything you need via Groovy. And the script could be run, of course, from within the IDE.

NetBeans IDE 6.1 Java Editor Refcard

Here it is:

Click the above or click this link: http://refcardz.dzone.com/announcements/netbeans

It specifically covers the Java editor (no way could it cover everything the IDE offers, so choices had to be made), future NetBeans cards could cover other languages (Ruby, PHP, Groovy, etc) or specific technologies (Mobility, Java EE, etc), for which proposals are welcome. Also, it is specifically focused on NetBeans IDE 6.1, an update will be published when 6.5 is released. Suggestions for improvements (and corrections) that will then be implemented in that release are welcome. Thanks very much to the reviewers (listed at the end of the card) and Jill Tomich from DZone for being patient and great to work with.

Friday Jul 25, 2008

Idiomatic Groovy

The code shown in my screenshot yesterday was correct Groovy but not idiomatic Groovy. This was the method in question:
String setSearchString(searchString) {

    def xml = proxy.GetSpeech(searchString)
    def XmlParser parser = new XmlParser()
    def speech = parser.parseText (xml)
    "PLAY: " + speech.PLAY.text() +
      "\\nSPEAKER:" + speech.SPEAKER.text() +
      "\\nTEXT:" + speech.text()

}

In Java, new lines need to be closed with a quote and contain the "+" character. In Groovy, although the above is perfectly acceptable, one can use triple-quotes for multi-lines. Even nicer, in this case, is to use GStrings (expressions declared inside double-quotes), together with Expandos (dynamic collections), instead:

String setSearchString(searchString) {

    def xml = proxy.GetSpeech(searchString)
    def XmlParser parser = new XmlParser()
    def speech = parser.parseText (xml)

    Expando result = new Expando()
    result.play = "PLAY: ${speech.PLAY.text()}"
    result.speaker = "SPEAKER: ${speech.SPEAKER.text()}"
    result.text = "TEXT: ${speech.text()}"
    result.all = "$result.play\\n$result.speaker\\n$result.text"

}

Much neater, more readable, and the result is the same as before. Plus, it could probably be improved even further.

Thursday Jul 24, 2008

Parsing XML with Groovy

The web service looked at yesterday returns a Shakespeare speech in XML format based on the search string that is sent via Groovy from a Swing form created in Matisse. Groovy isn't only useful in interacting with the web service—parsing the returned XML is also a trivial activity with Groovy:

As shown yesterday, the Groovy method shown above is called from the button in the form, that you see deployed in the screenshot above. On the return of the XML, Groovy takes a tiny number of lines to break up the payload and then (without the "return" statement being necessary, as pointed out in the comments yesterday by Alex Tkachman) is returned to the Swing form.

Wednesday Jul 23, 2008

Groovy Makes Web Services Embarrassingly Easy

Meera pointed me to another very cool article she's written, RESTful Web Services in 60 Seconds, and (because I've been in an intensely groovy frame of mind for a few days now), I immediately associated it with my earlier experiments with Groovy and web services, in a blog entry entitled Groovy Web Service from the end of last year, based on some key learnings from the official Groovy web service site, (which has expanded a lot since then, I noticed today). At the time, I needed to hack things quite a bit to get web services to work with Groovy in NetBeans IDE (as honestly recorded in that blog entry).

So, I thought, how do things stand today in terms of Groovy and web services in NetBeans IDE? To be perfectly honest, the improvement couldn't have been much better, aside from the currently incomplete code completion (which is a work in progress still). In fact, I was able to mix and match Matisse with a Groovy web service, although you can't tell from the result:

This is the actionPerformed in the JButton in the Matisse form that you see above:

ShakesWSClient shakes = new ShakesWSClient();
    
private void searchButtonActionPerformed(java.awt.event.ActionEvent evt) {                                         
    String searchString = shakes.setSearchString(searchTextArea.getText());
    resultTextArea.setText(searchString);
}

And this is the ENTIRE web service client:

package org.me.hello

import groovyx.net.ws.WSClient

class ShakesWSClient {

    def proxy = new WSClient("http://www.xmlme.com/WSShakespeare.asmx?WSDL", ShakesWSClient.class.classLoader)
	
    String setSearchString(searchString) {
        def newQuote = proxy.GetSpeech(searchString)
        return newQuote
    }

}

Really, that's just a bit ridiculous. And I'm sure that the Groovy experts could cut the above code down a few lines and characters further. And I didn't need to hack anything in NetBeans. The Groovy class behaved seamlessly with the Java class.

Tuesday Jul 22, 2008

Creating a Grails Plugin in NetBeans IDE

Let's create a plugin for Grails. Grails is, after all, modular and pluggable. Here's the ultimate simple Grails plugin, just to give an idea what is involved, from start to finish. The most useful references I have found so far are these:

Between those three, you should have enough to figure things out. I still found it hard, despite those instructions and so to avoid having to figure things out again some time in the future, I'll write absolutely everything here.

Creating the Plugin

  1. On the command line, run this:
    grails create-plugin SamplePlugin

    Now you have a Grails plugin. However, at the same time it is just another Grails application, which means you can simply open it in NetBeans IDE. (I.e., there is no import process and no NetBeans artifacts are added to the plugin in order to be able to open it in the IDE.)

  2. So open the plugin in the IDE. The Projects window isn't very interesting, it just shows you the same as you would normally see for Grails applications:

    The Files window (Ctrl-2) however, shows a lot more:

    Open the "SamplePluginGrailsPlugin.groovy" file and there you see the following:

    class SamplePluginGrailsPlugin {
        def version = 0.1
        def dependsOn = [:]
    	
        def doWithSpring = {
            // TODO Implement runtime spring config (optional)
        }
       
        def doWithApplicationContext = { applicationContext ->
            // TODO Implement post initialization spring config (optional)		
        }
    
        def doWithWebDescriptor = { xml ->
            // TODO Implement additions to web.xml (optional)
        }
    	                                      
        def doWithDynamicMethods = { ctx ->
            // TODO Implement registering dynamic methods to classes (optional)
        }
    	
        def onChange = { event ->
            // TODO Implement code that is executed when this class plugin class is changed  
            // the event contains: event.application and event.applicationContext objects
        }
                                                                                      
        def onApplicationChange = { event ->
            // TODO Implement code that is executed when any class in a GrailsApplication changes
            // the event contain: event.source, event.application and event.applicationContext objects
        }
    }

    I.e., you have hooks for integrating your code into meaningful places in the plugin.

  3. Now we'll create code that will let our plugin provide a new "constraint". (If you don't know what that is, you will know by the time you finish reading all this.) To do so, we will need to extend org.codehaus.groovy.grails.validation.AbstractConstraint, in a package within src/groovy:
    import org.codehaus.groovy.grails.validation.AbstractConstraint
    import org.springframework.validation.Errors
    
    class BestFrameworkConstraint extends AbstractConstraint {
    
        private static final String DEFAULT_MESSAGE_CODE = "default.answer.invalid.message";
        public static final String NAME = "oneCorrectResponse";
    
        private boolean validateConstraint
    
        //The parameter which the constraint is validated against:
        @Override
        public void setParameter(Object constraintParameter) {
            if (!(constraintParameter instanceof Boolean))
                throw new IllegalArgumentException("Parameter for constraint ["
                        + NAME + "] of property ["
                        + constraintPropertyName + "] of class ["
                        + constraintOwningClass + "] must be a boolean value");
            this.validateConstraint = ((Boolean) constraintParameter).booleanValue()
            super.setParameter(constraintParameter);
        }
    
        //Returns the default message for the given message code in the current locale:
        @Override
        protected void processValidate(Object target, Object propertyValue, Errors errors) {
            if (validateConstraint && !validate(target, propertyValue)) {
                def args = (Object[]) [constraintPropertyName, constraintOwningClass,
                        propertyValue]
                super.rejectValue(target, errors, DEFAULT_MESSAGE_CODE,
                        "not." + NAME, args);
            }
        }
    
        //Returns whether the constraint supports being applied against the specified type:
        @Override
        boolean supports(Class type) {
            return type != null && String.class.isAssignableFrom(type);
        }
    
        //The name of the constraint, which the user of the plugin will use
        //when working with your plugin.
        @Override
        String getName() {
            return NAME;
        }
    
        //Validate this constraint against a property value,
        //In this case, ONLY "Grails" is valid, everything else will cause an error:
        @Override
        boolean validate(target, propertyValue) {
            propertyValue ==~ /\^Grails$/
        }
    
    }

  4. Next, back in the Groovy plugin class that we looked at earlier, hook the above class into the plugin, using the "doWithSpring" closure to do so:
    def doWithSpring = {
        ConstrainedProperty.registerNewConstraint(
            BestFrameworkConstraint.NAME,
            BestFrameworkConstraint.class);
    }

  5. Now, back on the command line, navigate to within the "SamplePlugin" folder. There, run the following:
    grails package-plugin

    Back in the IDE, examine the ZIP file that the above command created:

That ZIP file is your Grails plugin.

Installing the Plugin

Now we will install our plugin in a new application.

  1. First, create a new Grails application by going to the New Project wizard (Ctrl-Shift-N) and choosing Groovy | Grails Application. Click Next and type "SampleApplication" and then click Finish.

  2. After the IDE has finished running the "grails create-app" command for you, you will see the new application open in the IDE. Right-click it and choose "Plugins", as shown here:

  3. In the Grails Plugins dialog, notice that the list gets filled with many potential plugins that you might want to install, from the Grails plugins repository. Instead, we'll install our own. Click Browse and browse to the ZIP file that we created three steps ago and notice that it appears in the text field at the bottom of the dialog:

  4. Click "Install" and then a progress bar appears, ending with the plugin being installed. Notice that you can also uninstall it:

  5. Take a look at your application and notice (in the Files window) what's happened to the plugin. It's been unzipped, plus the ZIP file is still there. And all that's been done in the "plugins" folder. Nothing else has changed, which means that uninstallation is as simple as removing the folder from the "plugins" folder:

    Thanks to "convention over configuration", Grails knows exactly where everything is—so that, for example, the "plugin.xml" file that you see above, if found within the folder structure you see above, is the indicator to Grails that a plugin is available for use.

Using the Functionality Provided By the Plugin

  1. Let's now use our plugin. Create a domain class called "Quiz", after right-clicking the "Domain Classes" node and choosing "Create new Domain Class":

  2. Right-click the "Controllers" node and choose "Create new controller". Type "Quiz" and then click Finish. Use the Groovy editor to add one line for adding the scaffolding (and uncomment the other line):

  3. Back in the "Quiz" domain class, add your property and use the "oneCorrectResponse" constraint defined in your plugin, as shown here:

    Note: The "oneCorrectResponse" constraint that you see above is the name of the constraint defined in the plugin.

  4. And then add the message to the messages.properties file, which is within the "Messages Bundles" node:

  5. Run the application and you will see that your constraint will prevent anything other than "Grails" from being considered acceptable, when "Create" is clicked below:

Congratulations, you've created, installed, and used your first Grails plugin!

Monday Jul 21, 2008

Integrating Meera with Grails and NetBeans

My DZone colleague Meera Subbarao took the new Grails/Groovy support in NetBeans IDE for a spin, with great results. Part of her concluding remarks are: "If you are a Groovy or a Grails fan, download the latest version of NetBeans and give it a try. You can develop, test and run your Grails application without ever opening a command window." Click the pic below to read the whole story:

Sunday Jul 20, 2008

Grails: This Time With Tools

Here's how to get started with Grails in NetBeans IDE 6.5.

  1. Prepare the Environment

    • Read the Grails Quick Start, which presents the scenario that we will reproduce in the sections that follow.

    • Download and install the "Web & Java EE" distribution of NetBeans IDE 6.5 Milestone 1 or later. (Everything done below is done with the latest development build, which is probably the best thing to get for getting to know the IDE's Grails support, if you're okay with unexpected errors now and then.)

    • Download and unzip Grails.

    • In the IDE, go to the Options window and set the location of Grails in the "Groovy" panel. If you want to use Groovy, also set the location of Groovy there.

  2. Create the Application

    Choose the Grails Application project in the New Project wizard (Ctrl-Shift-N):

    Click Next and type the name of the project:

    Click Finish and you should see this:

    Expand the folders and have a look around:

    Also look at the generated files and notice that many of them have default values filled in:

  3. Create the Domain Class

    Right-click on the Domains classes node:

    Name the domain class "Book" and then fill in the following two lines within the generated class:

    String title
    String author

    You should now see this:

    Create some initial values in the Bootstrap.groovy class:

    new Book(author:"Stephen King",title:"The Shining").save()
    new Book(author:"James Patterson",title:"Along Came a Spider").save()

    Use Ctrl-Space to call up code completion and then have a look at the other ways of completing the statements above:

    You should now see this:

  4. Create the Controller

    Right-click the Controllers node:

    Type "Book" and notice that you are shown that the generated class will be called "BookController":

    Then comment out the one line generated within the braces and add this one:

    def scaffold = Book

    You should now see this:

  5. Run the Application

    Right-click the application and choose "Run". The application is deployed to Jetty, as you can see in the Services window:

    The URL is printed to the Output window. Paste it into a browser and then you'll see your application. Click the "BookController" link and you'll see this:

    Click New Book and then create a new entry:

    When you click Create, note that you can edit or delete the entry:

    ...and that the entry is reflected in the list of entries:

There are several other Grails-specific features, such as a Grails shell, support for installing Grails plugins, and menu items for things like Grails stats, upgrade, and WAR-file creation. I will blog about these soon.

Saturday Jul 19, 2008

Search the Guardian from a NetBeans Platform Application without Coding At All

By default, the Quick Search feature is hidden in NetBeans Platform applications. Below, I show how to enable it in the layer.xml file and also how to enable the web search provider (this provider searches Google and returns pages that match your search string), which is defined in the NetBeans sources but not declared by default in the NetBeans Platform:
<folder name="QuickSearch">

    <!-- Optionally, we've hidden the existing quick search providers: -->
    <file name="Actions_hidden"/>
    <file name="GoToOption_hidden"/>
    <file name="Help_hidden"/>

    <!-- Enabling the WebQuickSearchProviderImpl: -->
    <folder name="Guardian">
        <file name="org-netbeans-modules-quicksearch-web-WebQuickSearchProviderImpl.instance"/>
    </folder>

</folder>

<!-- Showing the Quick Search feature: -->
<folder name="Toolbars">
    <folder name="QuickSearch">
        <attr name="SystemFileSystem.localizingBundle" stringvalue="org.netbeans.modules.guardiansearch.Bundle"/>
        <file name="org-netbeans-modules-quicksearch-QuickSearchAction.shadow">
            <attr name="originalFile" stringvalue="Actions/Edit/org-netbeans-modules-quicksearch-QuickSearchAction.instance"/>
        </file>
    </folder>
</folder>

Finally, in our branding folder, we create the folder hierarchy below and set the two properties that you see there (read about these properties at the bottom of this page):

In the IDE, the above properties are hardcoded to the following, but for the NetBeans Platform they are undefined and hence need to be branded as the above:

quicksearch.web.site=netbeans.org
quicksearch.web.url_patterns=.\*netbeans\\.org/kb.\*,.\*wiki\\.netbeans\\.org/.\*faq.\*,.\*wiki\\.netbeans\\.org/.\*howto.\*,.\*platform\\.netbeans\\.org/tutorials.\*

You've done no coding at all, right? So now run the application and you'll be able to search the Guardian (or whatever you set in the branded Bundle file that you see above):

When you click an item above, the related article opens in your browser.

So here's all I did to be able to search Jon Stewart's show:

quicksearch.web.site=thedailyshow.com
quicksearch.web.url_patterns=.\*

And here's the result:

All of the above is possible from 6.5 Milestone 1 onwards.

Friday Jul 18, 2008

Quick Search API Sample

I noticed in recent builds that the appearance of the new 6.5 Quick Search feature has improved recently. It looks a lot better and provides more functionality. First, you can see in the final category below that there is a "..." elipsis if the list of results is longer than the length of the visible list:

So you can click on that elipsis and see all the results. Also, if you're looking at the screenshot above and wondering: "He typed 'tip' but several of the results do NOT have 'tip' in the title," then you should look at this screenshot:

Here you can see that the search string "lookup" is in NONE of the returned titles. However, I happen to know for a fact that "lookup" is mentioned in the content of each of the above items, which means that the search does more than a title search, it actually looks into the body of the help topics, which is great news.

Finally, the categories are shown like this, so that you can limit the search to the category of your choice:

And maybe you're wondering about the categories "User FAQs" and "NetBeans Zone" in the screenshots above? Those come from the sample I'm turning into a tutorial describing the API that makes the above integrations possible. It is to be called "Quick Search Integration Tutorial" and will hopefully be available at the beginning of next week. However, the sample code is already available in the Plugin Portal:

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

Remember that you'll need a recent 6.5 build (Milestone 1 should be fine, I think) if you want to play with the sample.

Wednesday Jul 16, 2008

My 1st Contribution to NetBeans API Javadoc!

For a very long time, Jaroslav Tulach has been asking me to add links to documentation (blogs as well as tutorials) to the NetBeans API Javadoc. I always said I would do so but then ended up being too scared—it seemed strange to add additonal Javadoc comments to code that someone else had written and I thought that somehow the build would break if I did so. (And I was also concerned that the documentation in question might be wrong or misleading, while the fact that it was linked from the Javadoc might imply that it was somehow official.)

A few days ago I promised to do so again, but this time we were in a pub and he recorded me making the promise that I would add two links the next day. So there was no way back and so two days ago I finally did it:

http://hg.netbeans.org/main/rev/55ff702a6ccd

And today you can see the result:

So, now you can expect to see a lot more cross references from NetBeans API Javadoc to various bits of documentation that should be helpful when implementing the API in question!

Notes for NetBeans Platform 6.5 (Part 2)

Another change, comparable to the registration of the action class within the layer.xml that I showed yesterday, is the way in which actions that are "always enabled" will be created & registered in 6.5. In 6.5, if you make this selection in the New Action wizard:

...everything in the wizard will be the same as it always was. I.e., in the next panel, you'll specify where the "always enabled" action should appear and then, in the panel that appears after that, you will specify the name of the class and the package. However, when you complete the wizard, you will see that the generated class looks exactly like this:

package org.netbeans.modules.demo;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public final class MyWonderfulAction implements ActionListener {
    public void actionPerformed(ActionEvent e) {
       // TODO implement action body
    }
}

And this is how the class will have been registered in the layer.xml by the wizard:

<filesystem>
    <folder name="Actions">
        <folder name="Edit">
            <file name="org-netbeans-modules-demo-MyWonderfulAction.instance">
                <attr name="SystemFileSystem.localizingBundle" stringvalue="org.netbeans.modules.demo.Bundle"/>
                <attr name="delegate" newvalue="org.netbeans.modules.demo.MyWonderfulAction"/>
                <attr name="displayName" bundlevalue="org.netbeans.modules.demo.Bundle#CTL_MyWonderfulAction"/>
                <attr name="iconBase" stringvalue="org/netbeans/modules/demo/icon.png"/>
                <attr name="instanceCreate" methodvalue="org.openide.awt.Actions.alwaysEnabled"/>
                <attr name="noIconInMenu" stringvalue="false"/>
            </file>
        </folder>
    </folder>
    <folder name="Menu">
        <folder name="Edit">
            <file name="org-netbeans-modules-demo-MyWonderfulAction.shadow">
                <attr name="originalFile" stringvalue="Actions/Edit/org-netbeans-modules-demo-MyWonderfulAction.instance"/>
                <attr name="position" intvalue="100"/>
            </file>
        </folder>
    </folder>
</filesystem>

In other words, the "always enabled" action will, from 6.5 onwards, assuming you use the wizard, delegate to a plain old standard Java ActionListener class. And you will be able to focus on the purpose of the action within the class, while all the ancillary information is found within the layer.xml file (take special note of the new bundlevalue attribute), i.e., these properties are available even without the object having been instantiated. (You can go on using CallableSystemAction or AbstractAction if you want, but that's not code that the wizard will generate for you.)

However, while the situation where we don't have code completion in the layer.xml file continues, it will be hard to know exactly which attributes are available. (This has always been a problem, but is exacerbated now that even more is found in that file than before.) In that sense, we currently have better support for Spring RCP!

In other news. One of the current best blogs in the NetBeans world is the newish one by the NetBeans PHP development team: http://blogs.sun.com/netbeansphp/. The actual engineers in that team write that blog, with new developments as they happen. Bookmark it! One of the best blog entries I've seen (ever) in the software development world comes from there, click here to read it and see how immediate the interaction between users and engineers is and how directly one can impact the developments of the PHP team.

Tuesday Jul 15, 2008

Notes for NetBeans Platform 6.5 (Part 1)

Following on from Notes for NetBeans Platform 6.5 Milestone 1, let's look at some more and some slightly subtle changes in the upcoming 6.5 release, when creating new modules (i.e., plugins). I'm using a build from earlier today, you won't find much of what I describe here in earlier builds.

So, first, as always, we start up the New Module wizard. Look, our first change. A checkbox allowing you to enable/disable creating of the layer.xml file, which solves the problem introduced in 6.1 where the problem of modules with empty layer.xml files was solved by not prefilling the XML Layer field, which caused the problem where many people accidentally didn't create it even when they needed it and were confused later:

OK. Now let's create a new window. We use the New Window Component wizard and see a new checkbox, which I've mentioned before but which I will demonstrate in this blog entry. It is called "Keep preferred size when slided-in":

Select it and then click Finish. In the generated TopComponent constructor you'll find this line:

putClientProperty("netbeans.winsys.tc.keep_preferred_size_when_slided_in", Boolean.TRUE);

However, if the selected "Window Position" was "editor", which cannot be slided-in (i.e., minimized), then the above checkbox cannot be selected:

However, look at the generated files:

Where's the Action class? Not there! So how will you open the window? Before, the IDE always generated an action class and registered it in the layer.xml file. This time however, i.e., from 6.5 M2 onwards, you'll see the action declared in the layer file, without being present in your code at all (although you could continue doing things as before, just the wizard won't generate the old code for you):

<filesystem>
    <folder name="Actions">
        <folder name="Window">
            <file name="org-netbeans-modules-demo-DemoAction.instance">
                <attr name="SystemFileSystem.localizingBundle" stringvalue="org.netbeans.modules.demo.Bundle"/>
                <attr name="component" methodvalue="org.netbeans.modules.demo.DemoTopComponent.findInstance"/>
                <attr name="displayName" bundlevalue="org.netbeans.modules.demo.Bundle#CTL_DemoAction"/>
                <attr name="instanceCreate" methodvalue="org.openide.windows.TopComponent.openAction"/>
            </file>
        </folder>
    </folder>
    <folder name="Menu">
        <folder name="Window">
            <file name="DemoAction.shadow">
                <attr name="originalFile" stringvalue="Actions/Window/org-netbeans-modules-demo-DemoAction.instance"/>
            </file>
        </folder>
    </folder>
    <folder name="Windows2">
        <folder name="Components">
            <file name="DemoTopComponent.settings" url="DemoTopComponentSettings.xml"/>
        </folder>
        <folder name="Modes">
            <folder name="navigator">
                <file name="DemoTopComponent.wstcref" url="DemoTopComponentWstcref.xml"/>
            </folder>
        </folder>
    </folder>
</filesystem>

Look at the above tags carefully and see how the definition of the actions class is handled within the layer.xml file itself, which kind of reminds me of how things are done in Spring RCP.

Finally, when you install the module, you'll see the "Demo" menu item as before and you'll also see that when the window is slided-in (i.e., minimized), its size is now determined by its preferred size property, which means you have control over it for the first time:

There are other related changes too, which I'll discuss soon.

Thursday Jul 10, 2008

If you ever wrote to nbpodcast AT netbeans DOT org...

...please send your e-mail there again! Until a few minutes ago, the e-mail account wasn't working correctly, so that we never received any of your e-mails. (Which explains why "nobody solved the last NetBeans Puzzler".) Apologies for the inconvenience.

Use that mail address for (1) sending answers to the NetBeans Puzzler of the Podcast and (2) sending ideas for people/topics you'd like to have addressed in future podcasts!

Wednesday Jul 09, 2008

NetBeans Podcast Episode 44

The first NetBeans podcast hosted by Lloyd and myself!

NetBeans Podcast Episode #44 (31 min, size: 32.8 MB)

Send the answer to the puzzler to: nbpodcast@netbeans.org by Thursday, 31 July 2008.

If there are topics/people you'd like to hear about in future podcasts, please also let us know at nbpodcast@netbeans.org.

To subscribe via iTunes or similar players, visit http://feeds.feedburner.com/NetBeansPodcast.

JFugue Music NotePad & the NetBeans Platform Setting

When I check out http://nbjfuguesupport.dev.java.net/ and I then build the suite, I see the following in the Output window:
/home/geertjan/Desktop/nbj/nbjfuguesupport/build.xml:7: The following error occurred while executing this line:
/home/geertjan/Desktop/nbj/nbjfuguesupport/nbproject/build-impl.xml:19: You must define 'nbplatform.NetBeans_IDE_5.5.1_(Build_200704122300).harness.dir'

To fix this:

  1. Right-click the "Music NotePad" node in the Projects window.
  2. Choose Properties and then go to the Libraries node.
  3. In the "NetBeans Platform" drop-down, browse to the main folder of NetBeans IDE 5.5. Click Next and Finish.
  4. In the "NetBeans Platform" drop-down, make sure that you now see "NetBeans IDE 5.5", together with the name of some 5.5 build. Then click OK.
  5. Now run the application and it will start without a problem.

In other words, the message above appears if you haven't set the platform for the JFugue Music NotePad. The platform (i.e., the NetBeans Platform) provides the underlying modules that the application is built on top of. Currently, the platform used by the application is NetBeans Platform 5.5 (or 5.5.1), which doesn't mean that you can't use NetBeans IDE 6.x for development purposes, of course.

Tuesday Jul 08, 2008

Mixing and Matching Groovy and Java

Using 6.5 Milestone 1:

The result when you run the application above:

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
« July 2008 »
SunMonTueWedThuFriSat
  
4
5
6
11
12
13
14
17
26
30
  
       
Today