A system tray icon for monitoring Hudson with Eclipse RCP

Our team needed a system tray icon that indicates the status of the most recent build in Hudson. I decided to write a small application with Eclipse RCP which took me about a day.

Why Eclipse RCP? First, I didn't want to get involved with .NET. I only have the slimmest .NET knowledge and I didn't want to loose time by my lack of experience. Secondly, some of us have a Mac :-)

Thirdly, system tray icons can be added from the JVM. Java 6 supports it as does Eclipse Europa (3.3). I did a quick poll in our team and the majority wanted to avoid installing Java 6 on their machines. And some of us have a Mac :-)

So I ended creating my first application ever with Eclipse RCP. To get started I got the latest Eclipse release and followed the instructions to create a new RCP project here. Next I copied the system-tray-related code I found here.

That got me started. The first thing I did was obtaining some interesting icons to indicate a success and failed build. I found them here.

Plugin development (Eclipse considers an RCP application as a kind of plugin) in Eclipse is very intuitive, the Eclipse GUI makes things easy enough, like launching the application from Eclipse. Obviously debugging the application is very easy as well.

Based on the generated classes I've added all my logic to the ApplicationWorkbenchWindowAdvisor class. The first annoyance I ran into is Eclipse's threading model. Hudson has an RSS feed that contains the status of the latest build. I wanted to start a new thread to regularly check the RSS feed for updates.

But you cannot start a new thread and call Eclipse GUI components from it. Instead you have to call the org.eclipse.swt.widgets.Display#asyncExec(Runnable) method to execute code in a separate thread. But that's not all. I first implemented my Runnable class like this:

Runnable checkFeed = new Runnable() {
    public void run() {
        while (true) {
            // check the feed
            try {
                Thread.sleep(30 * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

window.getShell().getDisplay().asyncExec(checkFeed);

This however did not work out since the endless while loop blocks all GUI activity, including the popup menu when right-clicking on the system tray icon.

I had to stop blocking this thread, so I implemented the Runnable class as follows:

private long lastFeedCheck = 0;

private void launchFeedCheckingThread() {
    Runnable checkFeed = new Runnable() {
        public void run() {
            long now = System.currentTimeMillis();
            if (now - lastFeedCheck < 30000) {
                // sleep a little while to avoid being a CPU hog
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                // re-schedule this Runnable instance
                if (!window.getShell().getDisplay().isDisposed()) {
                    window.getShell().getDisplay().asyncExec(this);
                }
                return;             
            }
            try {
                // check the feed
            } finally {
                // update the timestamp variable to delay the
                // next feed check with 30 seconds
                lastFeedCheck = System.currentTimeMillis();
                // re-schedule this Runnable instance
                if (!window.getShell().getDisplay().isDisposed()) {
                    window.getShell().getDisplay().asyncExec(this);
                }
            }
        }
    }

    // launch the feed checking thread for the first time
    window.getShell().getDisplay().asyncExec(checkFeed);
}

This effectively frees up the thread with GUI activity responding promptly. While the RCP application behaved nicely when launched from Eclipse I ran into trouble when exporting the stand-alone application.

In Eclipse, exporting an RCP application is pretty straightforward. First, you have to create a 'Product Configuration' file (New -> Other -> Product Configuration). Here you can provide deployment options, like adding additional Eclipse plugins.

This worked fine until I added two libraries to the application: rome.jar and jdom.jar. I'm using them to read the Hudson RSS feed. I first added them as a project library in Eclipse as you would do in a regular Java project.

This however is not enough to have them exported. I tried adding them to the build.properties file under the Extra Classpath Entries section. This solved the error that previously occurred while exporting.

But the JARs still weren't included in the exported application. Unable to find another work-around I decompressed the content of both JARs in the root of the RCP project and included them for export in the build.properties file (see screen shot). This effectively included the required classed in the exported application :-)

Overall my first project in Eclipse RCP was a breeze. The Eclipse API is somewhat crowded but still intuitive to program with. The fact that the Eclipse classes have the source code attached makes things easier as well.

If you want to monitor you own Hudson server from your system tray check out the source code (Eclipse 3.3 plugin edition required) and Windows binaries.

Happy coding!

0
AttachmentSize
HudsonTray-win32.zip12.11 MB
HudsonTray-source.zip685.13 KB
ApplicationWorkbenchWindowAdvisor.java6.93 KB

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Paul Dinesh replied on Wed, 2008/01/23 - 10:45pm

Hi, I am not able to download the resources(attachments)...

Any Help on that??

Thanks,

paul

Steven Devijver replied on Thu, 2008/01/24 - 4:45am

Hey Paul, I just downloaded all files without problems. Can you try again please? Do you get an error message? Which one? Steven

Mike Evans replied on Fri, 2008/01/25 - 12:01pm

Steven, Congrats on your first RCP app! A couple of pointers for improving it: - the Display.asyncExec(..) does not run the Runnable in a seperate thread - it just posts it for the GUI thread to consume later (as I think you discovered!) - so you are right to use a background thread - however in RCP the better way to do it is to use the excellent 'Job' functionality - see 'Concurrency Infratstructure' in the Eclipse Help file for an excellent overview. Hope this helps, Mike Evans Incremental Ltd. www.incremental.eu.com

Cole Markham replied on Fri, 2008/01/25 - 12:40pm

Steven,

Nice work. Like MikeE said, the Job API is really nice for running a background task. Once you get your results you'll have to then use Display.asyncExec to update the UI.

Job feedCheckJob = new Job(){
// Check the feed
if(hasUpdates){
Display.getDefault().asyncExec(new Runnable(){
// Update display
});
}
this.schedule(3000); // Schedule to run again after a three second delay
}
feedCheckJob.schedule(); // Submit the job to be run now

I've run into that problem with the jars as well. What you need to do is add them to the the plugin manifest. Just open the editor for the META-INF/Manfiest.mf file that should have been created with your plugin. Then switch to the "Runtime" tab. In the bottom right there is a pane for adding jars; select the ones you need there. Then be sure you add the jars to the binary build like you did before. That should do the trick. You no longer need to specify the jars in the normal Java build path like you did before. I think it uses the Java build path (if it is there) when you run the application from Eclipse, which is why that worked. When you export the application it doesn't include that information, so it relies on what is in the bundle manifest.

Hope that helps.

 

Cole

Steven Devijver replied on Sat, 2008/01/26 - 6:35am

Thanks for the excellent advice guys!

Kohsuke Kawaguchi replied on Thu, 2008/01/31 - 3:49am in response to: sdevijver

Hi, I wonder if you'd be interested in hosting the source code in the Hudson project, so that other people can potentially join the development of this tool. If you'd be intereste, please write to me at kk AT kohsuke DOT org.

Toby Weston replied on Sat, 2008/09/13 - 10:33am

Hi, Great article, did you have sucess running on Mac with this? I'm having problems with notifications (balloon help) with something similar.

 The SWT snippet (http://dev.eclipse.org/viewcvs/index.cgi/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet225.java?view=co) just plain don't work for me on Mac, any ideas?

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.