Download NetBeans!

20080509 Friday May 09, 2008

Navigate Between Marked Occurrences

One of the dozens of interesting things I've picked up at JavaOne is... a very cool new keyboard shortcut that is in post-6.1 development builds. I.e., this is not in 6.1. You'd need to get a development build. Once you've done so, put your cursor on an identifier in your Java code, as shown below:

Great, all the occurrences of the selected identifier are marked, as in 6.0 and 6.1. Now how do you navigate between those marked occurrences? Wouldn't it be cool if you could hold down the Alt key and then use the Up arrow and Down arrow to navigate up and down the occurrences that have been marked? You can't do that in 6.1, but in development builds, that's exactly what you can do.

Vladimir Voskresensky from the NetBeans C/C++ development team in St. Petersburg told me about this on NetBeans Day. It's something he committed to the NetBeans sources, after having initially created it for the C++ editor. Thanks Vladimir. One thing I noticed is that the selected identifier suddenly changes while I am navigating, so that I can't navigate all the way down the list because the marked occurrence suddenly changes.

May 09 2008, 08:22:18 AM PDT Permalink

Download NetBeans!

20080508 Thursday May 08, 2008

https://nbrichfacessupport.dev.java.net

Go to https://nbrichfacessupport.dev.java.net/index.html and you will be able to read all about this new project and check out the sources. Join the project and contribute, especially if you have cool snippets that should be added to the RichFaces palette.

I will start populating the Issue database with new requests for enhancements soon.

May 08 2008, 08:16:51 AM PDT Permalink

Download NetBeans!

20080506 Tuesday May 06, 2008

RichFaces for NetBeans IDE 6.1 (Part 3)

I did a short demo of RichFaces support during NetBeans Day yesterday. It went without a hitch, producing this result in the browser:

Note especially the "Zoom" slider at the bottom of the page, which works as you would expect, i.e., the map is zoomed as the slider position changes. Here's the editor, containing everything needed for the above, while also showing the palette with two new items, one for the map and one for the slider:

I made several small changes to the support modules, such as a new JSP template which contains the taglib declarations for RichFaces (as requested by Wouter in the blog comments recently). Finally, I requested a project called 'nbrichfacessupport' on java.net, so watch this space about announcements around that and also for opportunities in that regard for getting involved!

May 06 2008, 07:09:59 AM PDT Permalink

Download NetBeans!

20080504 Sunday May 04, 2008

RichFaces for NetBeans IDE 6.1 (Part 2)

In the comments to my announcement about RichFaces in NetBeans IDE 6.1, yesterday, someone asked for tag completion. Well, that's already possible automatically when you install the plugin I mentioned yesterday. Since the libraries are on the classpath and the necessary declarations are generated right into the JSP page, you can immediately use tag completion (Ctrl-Space) in the generated page, as well as in any other JSP page where you add the declarations, which will result in you getting documentation as well as tag completion:

Also, you can use code completion to access managed beans...

...as well as their content:

Secondly, I created the start of a RichFaces palette, which you can get here:

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

Currently only two code snippets for RichFaces are in the palette (Aristotle Onassis and Donald Trump, haha) representing a RichFaces calendar and a RichFaces ComboBox. The tooltip shows exactly what will be generated when you drag/drop the item into the JSP page:

I am no RichFaces expert (having only heard of it the first time 2 days ago), so please tell me what else to add, any code snippets that you think are essential in this context, just leave a message at the end of this blog entry and I will extend the palette. Also, note that you can manually add your own snippets to the RichFaces palette, if you follow the instructions on the abovementioned plugins page.

May 04 2008, 07:06:23 AM PDT Permalink

Download NetBeans!

20080503 Saturday May 03, 2008

RichFaces for NetBeans IDE 6.1 (Part 1)

Yesterday I met with Wouter van Reeven, here in San Francisco, who mentioned the RichFaces framework to me. In particular, people in a JBoss-oriented world are likely to want to use RichFaces (as well as ICEfaces, Seam, and Facelets). For ICEfaces, a NetBeans plugin exists (here), though it works in 6.0 only. I've tried it, it's a pretty cool thing: you get support both for traditional web applications and for Visual Web applications. I'm hoping they'll upgrade to 6.1 soon (shouldn't be hard, I expect they only have a dependency change to fix).

However, no such thing exists for RichFaces. What's the difference between ICEfaces and RichFaces? No idea. Didn't know they existed until yesterday. However, they piqued my interest because of AJAX, which is a theme at NetBeans Day on Monday. So I created some basic NetBeans support for RichFaces, based on a cool and simple example application that Wouter sent me today. Go here to download the new support I created and then read on to learn about what it can do for you:

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

Download that ZIP file, you will have two NBM files in the ZIP file, install them both in NetBeans IDE 6.1.

Now do the following:

  1. Create a new web application.

  2. Make sure to set GlassFish as your server, because this server bundles JSF libraries and RichFaces is an extension of JSF. If you want to use Tomcat or some other server, make sure JSF is available to the server in question.

  3. In the Frameworks panel, choose Java Server Faces AND RichFaces:

    Click Finish.

  4. Inspect the generated sources. In addition to your standard JSF application, you have the RichFaces JARs in the Libraries node, you have RichFaces entries in your web.xml, your welcomeJSF.jsp contains some RichFaces-specific code, and you have a managed JSF Java bean in a new package in your source structure, automatically registered in faces-config.xml. Here's the whole thing:

  5. Run the application and you have your first AJAX application via RichFaces:

    Notice that the text to the right of the field is automatically updated as you type in the field itself. That's AJAX, partial page refresh, faster response to the user, etc.

  6. Want to change the "skin" of the application? Drop this into the web.xml:

    <context-param>
        <param-name>javax.faces.STATE_SAVING_METHOD</param-name>
        <param-value>server</param-value>
    </context-param>
    <context-param>
        <param-name>org.richfaces.SKIN</param-name>
        <param-value>blueSky</param-value>
    </context-param>

    Run the app again and notice the "skin" is different:

Now continue learning about RichFaces via online resources. Next, I will create a palette that provides items that you can drag and drop into a JSP page, for RichFaces. However, I doubt I will ever create RichFaces support for Visual Web applications, because that's an area I have little knowledge (nor very much interest) in. If someone wants to help me, such as in the area of Visual Web, feel free to leave a message here and let me know!

May 03 2008, 11:08:53 PM PDT Permalink

Download NetBeans!

20080501 Thursday May 01, 2008

Wicket 1.3.3 Support for NetBeans IDE 6.1

Today we uploaded the latest NBMs of our Wicket support into the Plugin Portal:

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

There are several changes, some mentioned recently in this blog. Basically, the names of the generated templates are friendlier, the JARs are Wicket 1.3.3 instead of 1.3.0, some useless options have been removed from the Frameworks panel, the Wicket Stylesheet support is part of the generated code, the Wicket filter is used in web.xml instead of the Wicket servlet, a header panel is always created... so mostly quite simple enhancements that, I believe, will make the user experience a lot better. Click the above link, then the Download button on the Plugin Portal page, unzip the ZIP that you then get and install the three NBMs. There is no need to download Wicket JARs from the Wicket site, because one of the three NBMs provides these and registers them in the IDE when you install the NBM.

I've installed them into 6.1, though they should probably also work in 6.0. Here's a quick scenario that should show you most of the Wicket support provided by this plugin, together with some nice Wicket/Ajax integration:

  1. Create a new web application and choose Wicket in the Frameworks panel:

  2. When you click Finish you have a nice simple source structure to begin your adventures with Wicket:

    (From a NetBeans API point of view, the coolest thing about the above screenshot is that you see exactly that when you complete the wizard, i.e., the package opens automatically and the HomePage.java is also opened automatically, so that you can begin coding there right away. It's a small thing, but pretty cool.)

  3. Right-click on the package (not on the project node, else you'll come across a known bug in this plugin) and choose New | Other and then choose the Panel template from the New File dialog:

  4. Name your panel:

  5. Click Finish and you have the skeleton of a new panel, i.e., both the Java side and the HTML side:

  6. Add a text field to the HTML side of the new panel, with a Wicket ID that will connect the HTML to the Java side of the panel:

  7. On the Java side of the Country Panel, use Wicket's AutoCompleteTextField class, as follows, making sure to pass the 'countries' ID, which connects the Java side with the HTML side defined in the previous step:

    final AutoCompleteTextField field = new AutoCompleteTextField("countries", new Model("")) {
    
        @Override
        protected Iterator getChoices(String input) {
            if (Strings.isEmpty(input)) {
                return Collections.EMPTY_LIST.iterator();
            }
            List choices = new ArrayList(10);
            Locale[] locales = Locale.getAvailableLocales();
            for (int i = 0; i < locales.length; i++) {
                final Locale locale = locales[i];
                final String country = locale.getDisplayCountry();
                if (country.toUpperCase().startsWith(input.toUpperCase())) {
                    choices.add(country);
                    if (choices.size() == 10) {
                        break;
                    }
                }
            }
            return choices.iterator();
        }
    };

    Above, the bits in bold is Wicket, the rest is just standard JDK code for getting the country names for the available locales. Here we're just building up a collection that will be displayed in the auto complete text field that we are creating here. The collection could contain anything at all, but Wicket provides the class that will make the text field behave in a way that we've come to expect from Ajax.

  8. Now add the field to the panel, on the Java side, by adding the one line below that is in bold, in the constructor:

    CountryPanel(String id) {
        super(id);
        add(field);
    }
  9. Hurray. You've just defined your first reusable panel. Now let's actually make use of it. In the HomePage.html, add a new tag below the existing tag, i.e., add the tag that is in bold below:

    <html>
      <head>
        <title></title>
        <link wicket:id='stylesheet'/>
      </head>
      <body>
        <span wicket:id='mainNavigation'/>
        <span wicket:id='countryPanel'/>
      </body>
    </html>

    The Wicket ID you specify here could be anything, so long as it is matched by the Wicket ID we add to the Java side, in the next step.

  10. In the HomePage.java, instantiate the Country Panel, by simply adding the line in bold (all the rest was generated by the Frameworks panel in the Web Application wizard):

    package com.myapp.wicket;           
    
    import org.apache.wicket.model.CompoundPropertyModel; 
    
    public class HomePage extends BasePage {
    
        public HomePage() {
    
            setModel(new CompoundPropertyModel(this));
            add(new CountryPanel("countryPanel"));
    
        }
    }

    In the same way that you've instantiated the Country Panel above, you could do so anywhere else, such as in the generated Header Panel. You just need to make sure that the Wicket ID is the same on both sides, i.e., in the HTML file and in the Java file.

  11. Hurray, you're done. Deploy the application to the server of your choice. Notice that you now have an auto complete text field in your browser:

What have you learned? Firstly, you've learned that NetBeans IDE has cool support for Wicket (and you haven't seen everything yet, for example, when you refactor a Java class, the related HTML side will be refactored at the same time and you can cause a hyperlink to be created on the Wicket ID on the HTML side, which will let you open the Java side from inside the HTML page, plus the Navigator shows the Wicket tags in the page, plus there's Wicket samples in the New Project wizard). Secondly, you've learned about one of Wicket's Ajax classes (go here for more). Thirdly... how much JavaScript have you used in order to create a very typical Ajax component? Well... ummm... none. So, you can use Ajax without leaving the comfortable world of Java. Fourthly, in the debug mode, which is Wicket's default mode, there's a cool debug console right inside the HTML page, which provides a lot of useful information about the current session. Fifthly (but something you can't see here), if the browser doesn't support JavaScript, Wicket provides fallback behavior to handle this for you. Finally, isn't it cool that you can wrap your Ajax behavior in your own Wicket components and then reuse them, so easily?

May 01 2008, 09:57:16 AM PDT Permalink

Download NetBeans!

20080430 Wednesday April 30, 2008

Introducing "Feature on Demand"

As your application gets larger, the end user experiences two problems: the download size increases and performance slows down. Not necessarily so, but the likelihood of these problems increases as time goes by. That's where modular systems come in handy, because they let the end user choose the modules they want while discarding all those they don't need. However, that process is problematic. How does the end user know which module provides which features? And then there's the problem of the ui that you provide for making features available, which is often not optimal for all your users. Sure, you could let the application automatically install all features and all updates to all features. That might be useful for many of your users. However, not all users want all features and all updates. On the one hand, these users would like to be able to select which features (and which updates to features) they want, but they also want the process to be really simple.

Welcome to "feature on demand". That's a new NetBeans Platform concept that's in the process of being evolved. It aims to enable you to provide the user with a really lightweight application (i.e., very small download, very small footprint, very small number of modules, very small everything). However, despite the lightness of the application, all/most of its ui is already available. Then, whenever the end user wants to use a feature in the application, such as a particular window, the application will automatically install that window, in the background. How? By making a menu item (or other entry point) available which, when invoked, causes the related module to download in the background, install itself automatically into the application, and immediately make its ui elements (such as a new window) available to the end user.

Potentially three parties are involved in this process:

  • The end user. I.e., granny Smith at home, filling in her tax returns in an application created on top of the NetBeans Platform.

  • The "feature on demand" service consumer. This is the developer who makes use of an existing entry point in the application by plugging into it. For example, let's say the developer writes a module and puts it into an update center. The module provides a new window in the application. In a separate module that is part of the application's official distribution (i.e., or that is installed automatically, or in some very simple way) the developer specifies that the module should be installed on demand, i.e., only when granny Smith invokes the related menu item. This module, i.e., the module consuming an entry point, is very small (just one or two XML files, as you can see in the example screenshot below), whereas the module (or multiple modules, including external libraries) providing all the functionality is much larger. Hence, distributing entry point consumers with your application is much more economical than distributing fully fledged features. In effect, you're distributing fake ui (such as a menu item that appears to invoke a window, while in fact it also installs it), while the real feature will be installed when the fake ui is used.

  • The "feature on demand" service provider. By default, "feature on demand" entry points exist (at least, currently) for new actions and new project types. That means that the service consumer can make use of these two entry points out of the box. However, potentially, you'd want additional entry points for your own features. Possibly your feature is not related to a menu item or project type. For example, when granny Smith tries to hyperlink in NetBeans IDE from one location to another, the module that provides that feature could be installed in the background, if the service provider provides such an entry point to the service consumer. The service provider is not concerned with specific modules, i.e., the service provider doesn't know what modules the service consumers will want to make available. The service provider simply makes entry points into the application available. This person will have a very technical knowledge of the application, while the service consumer needs to do nothing other than provide XML files that define how the entry point should be consumed in a particular case. The service provider and the service consumer could be the same person. And, of course, granny Smith at home could be all three at once, although this is less likely.

The end user, i.e., granny Smith, only gets involved in this whole cycle when she selects the menu item (or whatever the service provider makes available to the service consumer to let granny Smith demand the feature). But what about the service consumer? How does the service consumer consume the entry point? Let's say the service consumer wants to make the Module Manager available to granny Smith, but only when granny Smith selects the menu item to invoke it. Note that the Module Manager is a window in the IDE, provided by a module that's in the Update Center in 6.0 and 6.1, but never installed by default. Here's all that the service consumer needs to do to provide a fake "Module Manager" menu item in the Window menu which, when clicked, will install the Module Manager in the background:

Here's the content of the "modulemanager.xml" file:

<?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>
    //We want to contribute a new menu item to the application:
    <folder name="Menu">
        //The new menu item will be within the Window menu:
        <folder name="Window">
            //The unique identifier of the menu item:
            <file name="org-yourorghere-modulemanager-ModuleManagerAction.instance">
                //The reference to the API to which we delegate the creation of the menu item:
                <attr name="instanceCreate" methodvalue="org.netbeans.spi.actions.support.Factory.delegate"/>
                //The reference to the service provider's action that will be invoked when the menu item is created:
                <attr name="delegate" methodvalue="org.netbeans.modules.autoupdate.featureondemand.api.Factory.newAction"/>
                //The module that will be installed when the menu item is selected:
                <attr name="codeName" stringvalue="org.netbeans.modules.modulemanager"/>
                //The localization bundle that contains the display name of the menu item:
                <attr name="SystemFileSystem.localizingBundle" stringvalue="org.yourorghere.modulemanager.Bundle"/>
                //The key in the localization bundle that defines the display name of the menu item:
                <attr name="ActionName" stringvalue="org-yourorghere-modulemanager-ModuleManagerAction" />
                //The position of the menu item within the Window menu:
                <attr name="position" intvalue="10"/>
            </file>
        </folder>
    </folder>
</filesystem>

The above looks a lot like a layer.xml file, doesn't it? That's because that's what it is. It is known as the "delegate layer file", which is injected into the System FileSystem under certain conditions. Which conditions? The conditions are specified in the layer.xml file:

<?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>
    //The official "feature on demand" folder in the System FileSystem:
    <folder name="FeaturesOnDemand">
        //The unique name of this entry in the "feature on demand" folder:
        <file name="org-yourorghere-modulemanager-ModuleManagerAction.instance">
            //The API class that holds the delegate layer file, with related information:
            <attr name="instanceCreate" methodvalue="org.netbeans.modules.autoupdate.featureondemand.api.FeatureInfo.create"/>
            //The presence of this plugin determines whether the delegate layer will be injected
            //i.e., if this plugin is not installed, the menu item will be shown, and vice versa,
            //this plugin could potentially be different to the plugin that the menu item will install:
            <attr name="codeName" stringvalue="org.netbeans.modules.modulemanager"/>
            //The location of the delegate layer file:
            <attr name="delegateLayer" urlvalue="nbresloc:/org/yourorghere/modulemanager/modulemanager.xml"/>
        </file>
    </folder>
</filesystem>

So, now, granny Smith, filling in her tax returns, would see "Module Manager" in the Window menu (if the Module Manager plugin isn't already installed). She would, if she selects the Module Manager menu item, see that the related plugin installs and then immediately makes the Module Manager available. Until that point, the Module Manager will not be installed in the application and thus will not be weighing down the application, in terms of download size, footprint, and performance. By the way, all of this assumes that the plugin that will be installed on demand is actually available in one of the registered update centers.

Everything required for implementing "Feature on Demand" is already available in the "contrib" module in the NetBeans sources. For more information on this, together with several demos, including how to provide a new entry point, come to the JavaOne BOF "Toward a Consumer IDE: Get What You Want When You Want It (BOF 5091)", on Wednesday 18.30-19.20. I will be presenting this BOF together with the engineer who conceptualized and implemented "Feature on Demand", the NetBeans update center guru Jiri Rechtacek.

Apr 30 2008, 04:05:52 AM PDT Permalink

Download NetBeans!

20080429 Tuesday April 29, 2008

org.openide.filesystems.FileUtil.findBrother

The org.openide.filesystems.FileUtil.findBrother method is typically used by the Matisse GUI Builder so that if a file named "xyz.java" is in the same folder as a file named "xyz.frm", the two are merged together and opened as one in the editor. Specifically, FileUtil.findBrother takes a FileObject and a file extension, which is the file extension of the "brother" file.

There are several other conceivable scenarios where this functionality might be handy, such as Wicket, which typically has "xyz.java" in the same folder as "xyz.html". The latter provides the markup for the former. Below you can see my point, the Java files that have a matching HTML file (i.e., the same name) have a Wicket icon merged with their Java icon:

So, first create a new DataObject for text/x-java—and make sure it will install before the standard Java DataObject. Then, use the DataLoader to branch the DataObject, depending on whether a "brother" is found:

@Override
protected MultiDataObject createMultiObject(FileObject primaryFile) throws DataObjectExistsException, IOException {
    FileObject bro = FileUtil.findBrother(primaryFile, "html");
    if (null != bro) {
        //Return our own data object:
        return new WicketDataObject(primaryFile, this);
    }
    //Return the standard data object:
    return new JavaDataObject(primaryFile, this);
}

Finally, in the Node, use org.openide.util.Utilities.mergeImages to merge a small image on top of the standard Java icon:

private static final String WICKET_ICON_BASE = 
         "org/netbeans/findbrotherdemo/wicket_8x8.png";
private static final String JAVA_ICON_BASE =   
         "org/netbeans/modules/java/resources/class.gif";
 
@Override
public Image getIcon(int arg0) {
    Image wicket = Utilities.loadImage(WICKET_ICON_BASE);
    Image java = Utilities.loadImage(JAVA_ICON_BASE);
    Image result = Utilities.mergeImages(java, wicket, 8, 8);
    return result;
}

Install the module and then Java files with HTML brothers will have our Wicket icon merged with their Java icon.

Apr 29 2008, 04:11:39 AM PDT Permalink

Download NetBeans!

20080428 Monday April 28, 2008

Reorganized & Simplified Wicket Support

In preparation for a JavaOne demo, I've simplified the NetBeans/Wicket support, from a ui and user perspective. This is what the Frameworks panel now looks like, after I removed some superfluous options (so that now the header panel is always created, while the useless option for the dummy pojo is removed) and changed some default names:

And when the user finishes the wizard, they will see exactly this, i.e., the source package folder will be open automatically and the HomePage class will be open in the editor, because that's probably the first place where you'll start coding. A default model setting is defined in the generated HomePage class and the org.apache.wicket.markup.html.resources.StyleSheetReference class is used in the BasePage class, to provide localized CSS support. Notice below also that the default names of the generated files have been changed and simplified, so that it's easier to see what's what, especially if you have some Wicket background:

For example, as you can see, pages and panels are easily distinguishable, now, because the name of the generated file (by default, anyway) contains the related info in this regard. Finally, no index.jsp is created, for the first time. The IDE's Frameworks support creates an index.jsp file by default, if no welcome file element is defined in the web.xml file by the module. So, I defined a welcome file element (even though it isn't used by Wicket) and so now the index.jsp is no longer created.

I need to do a bit more work on the module, such as upgrading the libraries to Wicket 3.3 (which fixes at least one important Ajax-related bug that I am aware of) and I'm hoping to have committed all my changes to CVS by the time JavaOne begins.

Apr 28 2008, 08:27:46 AM PDT Permalink

Download NetBeans!

20080427 Sunday April 27, 2008

Using Spring to Enable the Print Menu Item

Recently I blogged about Spring integration into the NetBeans module system. Here's how you would use that integration to enable the Print menu item for a TopComponent. In effect, what this example shows is how to use a Spring configuration file to extend a TopComponent's Lookup to include a PrintCookie:

  1. Implement PrintCookie, which requires a dependency on Nodes API:

    package org.yourorghere.nbspringdemo1;
    
    import javax.swing.JOptionPane;
    import org.openide.cookies.PrintCookie;
    
    public class PrintImpl implements PrintCookie {
    
        @Override
        public void print() {
            JOptionPane.showMessageDialog(null, "I am printing...");
        }
    
    }

  2. Create a bean for your PrintCookie in your Spring app-config.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans 
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
        <bean id="printCookie" class="org.yourorghere.nbspringdemo1.PrintImpl"/>
    </beans>

  3. Add to the TopComponent's constructor:

    //Bring in Spring:
    String[] contextPaths = new String[]{"org/yourorghere/nbspringdemo1/app-context.xml"};
    ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(contextPaths);
    
    //Convert Spring to Lookup:
    Lookup lookup = NbSpring.create(ctx);
    
    //Potentially, look up your JPanel and add to TopComponent:
    //Item<JPanel> item = lookup.lookupItem(new Template<JPanel>(JPanel.class, null, null));
    //JPanel foo = item.getInstance();
    //add(foo, java.awt.BorderLayout.CENTER);
    
    //Add node for PrintCookie:
    AbstractNode myNode = new AbstractNode(Children.LEAF, lookup);
    ProxyLookup proxyLookup = new ProxyLookup(Lookups.singleton(myNode), myNode.getLookup());
    associateLookup(proxyLookup);

That's it. Now the Print menu item is enabled whenever the TopComponent is activated. You could narrow it down further, basing it on whether something in the TopComponent has changed.

Thanks to Jaroslav Tulach for showing me how to do this.

Apr 27 2008, 03:05:47 PM PDT Permalink

Download NetBeans!

20080426 Saturday April 26, 2008

cismet Developers Will Code for Food!

The "Will Code for Food" competition takes us to... a small software company named cismet based in Saarland, a small state in Germany. The guys from cismet are the ones who created the "Log4J Logging Viewer plugin for NetBeans IDE". It is also known as BeanMill. On their Partners page, you can see that cismet is one of the NetBeans partners.

"Thanks for the nice idea," writes Sebastian Puhl, one of the cismet developers, "we had fun making the pictures for the competition." The whole series is in cismet's Flickr account. My personal favorite is this one:

Remember that the 1st Annual "Will Code For Food" Competition will end on Friday, 1 August 2008, and that the winner will receive a signed copy of Jaroslav Tulach's upcoming book on API design. Submissions to geertjan DOT wielenga AT sun DOT com.

Apr 26 2008, 08:44:03 AM PDT Permalink

Download NetBeans!

20080425 Friday April 25, 2008

NetBeans Lookup Example

Lookup example, producing a new message automatically every 2 seconds:

  • Provider module:

    public final class Selection {
    
        private Selection() {
        }
    
        private static MyLookup LKP = new MyLookup();
    
        //Make the Lookup accessible:
        public static Lookup getSelection() {
            return LKP;
        }
    
        private static final class MyLookup extends ProxyLookup
                implements Runnable {
    
            private static ScheduledExecutorService EX = 
                    Executors.newSingleThreadScheduledExecutor();
    
            public MyLookup() {
                EX.schedule(this, 2000, TimeUnit.MILLISECONDS);
            }
    
            private int i;
    
            @Override
            public void run() {
                //Add to the Lookup a new MyHello:
                setLookups(Lookups.singleton(new MyHello(i++)));
                EX.schedule(this, 2000, TimeUnit.MILLISECONDS);
            }
    
        }
    
        private static final class MyHello implements HelloProvider {
    
            private String text;
    
            public MyHello(int i) {
                text = i % 2 == 0 ? "Hello from Tom" : "Hello from Jerry";
            }
    
            public String sayHello() {
                return text;
            }
    
        }
        
    }

    public interface HelloProvider {
        public String sayHello();
    }

  • Consumer module, with dependency on the provider module:

    final class HelloTopComponent extends TopComponent implements LookupListener {
    
        private static HelloTopComponent instance;
        private static final String PREFERRED_ID = "HelloTopComponent";
        private Result result;
    
        private HelloTopComponent() {
            
            ...
            ...
            ...
    
            //We have a dependency on the provider module,
            //where we can access Selection.getSelection():
            Lookup lookup = Selection.getSelection();
    
            //Get the HelloProvider from the result:
            result = lookup.lookupResult(HelloProvider.class);
            
            //Add LookupListener on the result:
            result.addLookupListener(this);
    
            //Call result changed:
            resultChanged(null);
            
        }
    
        StringBuilder sb = new StringBuilder();
        int i;
    
        @Override
        public void resultChanged(LookupEvent arg0) {
            long mills = System.currentTimeMillis();
            Collection instances = result.allInstances();
            for (HelloProvider helloProvider : instances) {
                String hello = helloProvider.sayHello();
                sb.append(mills + ": " + hello + "\n");
                jTextArea1.setText(sb.toString());
            }
        }
    
       ...
       ...
       ...

Result:

Apr 25 2008, 07:54:49 AM PDT Permalink

Download NetBeans!

20080424 Thursday April 24, 2008

Vincent Cantin: "Will Show Hidden Folders For Food"

The first entry for the 1st Annual "Will Code For Food" Competition:

Possibly the above is a reference to The Mystery of the Hidden Folders in the Favorites Window and on the thread on which that blog entry is based.

So now the flood gates are open... more "code for food" photos are welcome at geertjan DOT wielenga AT sun DOT com.

Apr 24 2008, 11:41:00 AM PDT Permalink

Download NetBeans!

20080423 Wednesday April 23, 2008

Hello Code Generator

I click Alt-Insert and I see this:

Here's the code:

import com.sun.source.util.TreePath;
import java.io.IOException;
import java.util.Collections;
import javax.swing.JOptionPane;
import javax.swing.text.JTextComponent;
import org.netbeans.api.java.source.CompilationController;
import org.netbeans.modules.java.editor.codegen.CodeGenerator;

public class HelloGenerator implements CodeGenerator {

    public static class Factory implements CodeGenerator.Factory {

        public Factory() {
        }

        @Override
        public Iterable<? extends CodeGenerator> create
                (CompilationController controller, TreePath path) throws IOException {
            return Collections.singleton(new HelloGenerator());
        }
    }

    @Override
    public String getDisplayName() {
        return "Hello World";
    }

    @Override
    public void invoke(JTextComponent arg0) {
        JOptionPane.showMessageDialog(null, "Hello World!");
    }

}

Doesn't do anything yet, but gives you a starting point. Register it like this:

<filesystem>
    <folder name="Editors">
        <folder name="text">
            <folder name="x-java">
                <folder name="codegenerators">
                    <file name="org-netbeans-modules-my-codegen-HelloGenerator$Factory.instance">
                        <attr name="position" intvalue="10"/>
                    </file>
                </folder>
            </folder>
        </folder>
    </folder>
</filesystem>

Dependencies: Javac API Wrapper, Java Editor (implementation dependency, because the above is not a public API yet), and Java Source.

Apr 23 2008, 12:46:38 AM PDT Permalink

Download NetBeans!

20080422 Tuesday April 22, 2008

Converting Spring to Lookup

In the screenshot below, the content of the "Spring Window" is injected via a Spring configuration file:

The highlighted classes above are the Swing components and behavior that Spring injects into the TopComponent (except for 'SpringAction', which opens the window). In the editor area above, the following constructor is shown:

private SpringTopComponent() {

    initComponents();

    setName(NbBundle.getMessage(SpringTopComponent.class, "CTL_SpringTopComponent"));
    setToolTipText(NbBundle.getMessage(SpringTopComponent.class, "HINT_SpringTopComponent"));

    String[] contextPaths = new String[]{"org/netbeans/nbspringdemo/app-context.xml"};
    ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(contextPaths);

    Lookup lookup = NbSpring.create(ctx);
    Item item = lookup.lookupItem(new Template(JPanel.class, null, null));
    JPanel foo = (JPanel) ctx.getBean(item.getId()); 

    add(foo, java.awt.BorderLayout.CENTER);

}

The line in bold above is possible as a result of the new Spring/NetBeans API that is in 'contrib', as highlighted in my blog yesterday. That line is the thing that makes this possible at all. Without it, i.e., without being able to convert my Spring configuration file to Lookup, none of this would be possible. And what does my Spring configuration file look like? Exactly this:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
       http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
           
    <bean id="mainPanel" class="org.netbeans.nbspringdemo.MyJPanel" init-method="init">
        <property name="axis">
            <value>1</value>
        </property>
        <property name="panelComponents">
            <list>
                <ref bean="textField1"/>
                <ref bean="textField2"/>
                <ref bean="textField3"/>
                <ref bean="buttonPanel"/>
            </list>
        </property>
    </bean>

    <bean id="buttonPanel" class="org.netbeans.nbspringdemo.MyJPanel" init-method="init">
        <property name="axis">
            <value>0</value>
        </property>
        <property name="panelComponents">
            <list>
                <ref bean="button1"/>
            </list>
        </property>
    </bean>

    <bean id="textField1" class="org.netbeans.nbspringdemo.MyJTextField" init-method="init">
        <property name="text">
            <value>hello 1</value>
        </property>
        <property name="rColor">
            <value>255</value>
        </property>
        <property name="gColor">
            <value>51</value>
        </property>
        <property name="bColor">
            <value>102</value>
        </property>
    </bean>

    <bean id="textField2" class="org.netbeans.nbspringdemo.MyJTextField" init-method="init">
        <property name="text">
            <value>hello 2</value>
        </property>
        <property name="rColor">
            <value>0</value>
        </property>
        <property name="gColor">
            <value>100</value>
        </property>
        <property name="bColor">
            <value>0</value>
        </property>
    </bean>

    <bean id="textField3" class="javax.swing.JTextField">
        <property name="text">
            <value>goodbye world</value>
        </property>
    </bean>

    <bean id="button1" class="org.netbeans.nbspringdemo.MyJButton" init-method="init">
        <property name="actionListener">
            <ref bean="myButtonActionListener"/>
        </property>
        <property name="text">
            <value>Click me!</value>
        </property>
    </bean>

    <bean id="myButtonActionListener" class="org.netbeans.nbspringdemo.MyActionListener"/>

</beans>

That's the Spring configuration file that I discussed in Spring: How to Create Decoupled Swing Components on JavaLobby. The definition of the classes, i.e., the JPanel, JButton, and JTextField, are also described in that article. Read the comments to that article to see some use cases where you might want to assemble your user interface via decoupled Swing components and Spring.

Pretty cool that this is now also possible on the NetBeans Platform. I am not advocating this approach, I am merely pointing out that this is possible.

In other news. Read this blog entry about NetBeans Day Fortaleza!

Apr 22 2008, 05:49:51 AM PDT Permalink