Introduction to Hudson Plugin Development

Introduction to Hudson Plugin Development

By Dan Lewis, OCI Senior Software Engineer

August 2010


 

Introduction

Hudson is the excellent continuous integration server written by Kohsuke Kawaguchi and others. Integral to Hudson is a plugin mechanism that allows third-parties to extend Hudson in several ways, including reporting test failures, results of automated code inspections, notification of broken builds and publication of build artifacts. This article will introduce the reader to the process of developing and modifying Hudson plugins through an easy-to-follow set of instructions and illustrations. While not a comprehensive treatise on Hudson plugin development or architecture, this article will help get you started with plugin development. This article does assume basic familiarity with Hudson. For a suitable introduction, see Automated Builds Made Easy with Hudson.

The Hello World Builder Plugin

The Plugin Tutorial on the Hudson WIKI gives the steps to create the example Hudson Plugin using Maven and how to open up the Plugin using IDE's that support Maven project integration. This article supplements the wiki by providing additional illustrated instructions.

1. Obtain Maven 2 and add to PATH

Navigate to http://maven.apache.org and download the latest Maven 2 release. The author used Maven 2.2.1.

Maven 2.21

2. Run Maven command to create Hudson Plugin Workspace

This will connect to the central Maven repository and download the necessary Maven information to create the workspace, then create it.

mvn -cpu hpi:create
...
"Enter the groupId of your plugin:" com.ociweb.jnb.aug2010
"Enter the artifactId of your plugin:" hpiExample

After answering the prompts as shown, a new workspace will be created inside the directory hpiExample. The Java package com.ociweb.jnb.aug2010 will be used.

3. Build the plugin

This will build the plugin.

cd hpiExample
mvn package

This will build the file target\hpiExample.hpi.

4. Download and run Hudson

Before proceeding, we need to download and run Hudson so that we will have somewhere to deploy this new plugin. Navigate to http://hudson-ci.org and download the latest Hudson release. The author used Hudson 1.369.

Hudson 1.369


Create a directory, say "hudson" and copy the downloaded hudson.war to that directory and start it. This will start Hudson on the embedded Winstone servlet container.

mkdir hudson
cd hudson
java -jar hudson.war
Running from: C:\hudson\hudson.war
[Winstone 2010/07/27 06:34:58] - Beginning extraction from war file
hudson home directory: C:\Users\lewisd\.hudson
[Winstone 2010/07/27 06:34:59] - HTTP Listener started: port=8080
[Winstone 2010/07/27 06:34:59] - AJP13 Listener started: port=8009
Using one-time self-signed certificate
...

You may now navigate with your web browser to http://localhost:8080.

Hudson Running


5. Install the plugin

We can now install the plugin we built to the local Hudson instance we created. First, click to Manage Hudson.

Manage Hudson

 

Next, click to manage plugins.

Manage Plugins

 

Next, click Advanced tab.

Click Advanced

 

Next, upload the plugin.

Upload Plugin

 

Next, verify the plugin is installed. Note: You will need to stop and restart Hudson. On Windows, you may press Ctrl-C in the command window running Hudson.

Plugin Installed

 

Finally, we have the plugin installed.

6. Exercise the plugin

To exercise the plugin, let's check first for any global configuration options the plugin contributed.

Global Config

Observe that it does provide the option to say hello in French as well as help available if you click on the question mark icon.
 

To exercise the plugin further let's first create a new trivial job. From the main "Manage Hudson" page click the "Create New Job" link.

New Job Link

 

Next, in the job config screen, type a name for the job, choose free-style project, and press OK.

New Job

 

This will create a new Job and take you directly to the Job Configuration page. Scroll down to the Build section and click to add a new build step.

Add Build Step

 

Now we see the Job configuration options for the Hello World Builder, again with help available if you click on the question mark icon.

Job Config

 

Now, type a name and scroll to the bottom of the job configuration page and click "Save." Note that if your name is less than four characters long, i.e. "Dan", a warning will appear when you navigate away from the name field.

Name Too Short

 

Next, you can run the job.

Build Now

 

Next, you can see that the job ran and see the results. Click build #1 in this history.

Build History

 

Now from the build page, you can click to see the console output.

Click Build Output

 

Now we can see that our plugin performed it's build step by printing "Hello, Dan!".

Console Output

Customizing the Hello World Builder Plugin

In the previous section we built, installed, verified and exercised the Hello World plugin that is available for download from the central Maven 2 repository. In this section, we will make some minor changes to the plugin and observe the results. For this article, the free and open source IntelliJ IDEA Community Edition will be used. However, Eclipse, Netbeans, or even a simple text editor could be used.

1. Open the Hudson Plugin project

Open IDEA and click File->Open Project... from menu, then navigate to the pom.xml. IntelliJ IDEA recognizes the pom.xml as a Maven build file. Select "OK".

Idea open ProjectOpen Pom

2. Navigate to the HelloWorldBuilder class

Click to open the project pane on the left side of the IDE, then navigate to hpiExample/src/main/java/com/ociweb/jnb/aug2010/HelloWorldBuilder and double click on HelloWorldBuilder.java.

Navigate to Hello World Builder

3. Modify the Plugin

Now we're ready to make a small change. Because "Dan" is a common name, let's modify the name field validation to only warn if names are less than 3 characters long. To do this, we can edit the doCheckName() method.

/**
 * Performs on-the-fly validation of the form field 'name'.
 *
 * @param value
 *      This parameter receives the value that the user has typed.
 * @return
 *      Indicates the outcome of the validation. This is sent to the browser.
 */
public FormValidation doCheckName(@QueryParameter String value) throws IOException, ServletException {
    if(value.length()==0)
        return FormValidation.error("Please set a name");
    if(value.length()<3)
        return FormValidation.warning("Isn't the name too short?");
    return FormValidation.ok();
}

Note that if you look for usages of doCheckName() using the IDE's "find usages" feature, you won't find any. That is because Hudson is using Stapler to bind the validation of the name field to the doCheckName() method using Java reflection.

4. Build the Plugin and Observe Changes

At this point you can return to step three to rebuild, upload the updated plugin. However, a shortcut exists. Simply do mvn hpi:run. This will automatically start the Jetty servlet container with your plugin already deployed.

After Validation Change


Observe that we don't see the "Isn't the name too short?" validation message anymore when the "Name" field loses focus.

We have customized the Hello World plugin and learned the basic techniques for building and installing a plugin. In the next section, we'll modify a more useful plugin that the author has used in a production environment.

Modifying an Existing Plugin

One of the strengths of Hudson is the ease with which you can take an existing plugin and modify it to suit your purposes. To illustrate this strength we will modify the Text-Finder Plugin. The Text-Finder Plugin can search the workspace and/or console for files containing a given regular expression and optionally fail the build or mark the build unstable. The author has used this relatively simple plugin in a production environment and found it to be useful. We will enhance the Text-Finder Plugin to add a description indicating why the build failed or was marked unstable. To start, we will check out the Text-Finder Plugin from the source control repository. The wiki page Checking out existing plugins describes the process of checking out an existing plugin. We will utilize this information in the next step.

1. Check out the Text-Finder Plugin

For this step you will need a command-line svn client. On Windows, the author recommends SlikSVN.

Once you have installed a command-line svn client, you may issue the following command:

svn co https://svn.dev.java.net/svn/hudson/trunk/hudson/plugins/text-finder

2. Build the Text-Finder Plugin

We have now checked out the Hudson Text-Finder plugin into the text-finder directory. Now, let's build it.

cd text-finder
mvn package

This will build the file target\text-finder.hpi.

At this time you can install it into your Hudson instance as described above and exercise it inside the job we created in the previous section. This exercise is left to the reader.

3. Modify the Text-Finder Plugin

Now we can open the Text-Finder pom.xml using IntelliJ IDEA. Navigate to TextFinderPublisher.java and double-click to open. Scroll down to the end of the findText() method and modify the code to call the Build.setDescription() method. This method sets the description of an individual build of a project. The build description is visible in the Build History pane on the left side of the project page. It is also visible in the build detail page.

Before:

             if (foundText != succeedIfFound)
                build.setResult(unstableIfFound ? Result.UNSTABLE : Result.FAILURE);
        } catch (AbortException e) {
            // no test file found
            build.setResult(Result.UNSTABLE);
        }
    }

 

After:

            if (foundText != succeedIfFound) {
                build.setResult(unstableIfFound ? Result.UNSTABLE : Result.FAILURE);
                build.setDescription("build result set to " + build.getResult()
                		+ "; regex [" + regexp + "]" + (foundText ? " was " : " was not ") + "matched");
            }
        } catch (AbortException e) {
            // no test file found
            build.setResult(Result.UNSTABLE);
            build.setDescription("build result set to " + build.getResult()
            		+ " because Text-Finder aborted");
        }
    }

4. Observe the Changes

To observe the changes, edit the project configuration page and use "Scarecrow" as the name in Hello-World Plugin and also as the regular expression in the Text-Finder Plugin. Be sure the check the "Also search the console output" checkbox and the "Unstable if found" checkbox as shown in the image below.

Hello World and Text Finder

 

Now save the changes and build the project. You should see something like the following image.

Observe Changes 2

Conclusion

Hudson is a mature free and open-source continuous integration solution with a rich set of free third-party plugins available. With the help of this article you created and modified your own "toy" Hudson plugin and modified an existing "real" plugin. For further reading about Hudson Plugin development, please see the Hudson Wiki page Extend Hudson.

References