Tuesday, January 22, 2008

Writing a Hudson plug-in (Part 2 – Understanding m2 and freestyle projects)

Before we proceed with developing the plugin, there is an important concept to understand: How the different project types invoke their plugins.


Currently, one of the biggest source of confusion for end users of Hudson is that most of the plugins do not work with the "m2 project type". This is because there are essentially two completely different implementations of plugins.

"Normal" Plugins

"Normal" plugins work with all project types except the "m2 project type". Typically they will extend Publisher. The model of execution is that the Publisher is invoked on the Hudson Master and can invoke actions remotely on the slave.

The plugin author must be mindfull that they do not overload the master with excessive work, either by minimising the amount of work to be performed, or by sending work to the slave for execution. Best performance will be achieved if the heavy lifting is performed on the slave and then finally the results are sent to the master. Multiple trips back and forth between the master and the slave will not be performant.

"Maven" Plugins

"Maven" plugins only work with the "m2 project type". They must extend MavenReporter. Contrary to the "Normal" plugins, they are executed on the Slave. This is because they are invoked as call-backs from the Maven Embedded that is running on the slave. If the MavenReporter is able to invoke actions remotely on the Master.

The plugin author does not have to be as mindful about overloading the master, as by default, they are executing on the slave, however, the side effect is that the plugin does not have full access to all the Hudson objects, and must usually send work to the master, e.g. to add an Action to a build, it is necessary to serialize the Action, send it to the master along with a Callable that then takes the deserialized Action and adds it to the real Build. (Note: Adding an action is such a common task that BuildProxy provides convenience methods to do this for us)

Don't Repeat Yourself

The result of this split is that it can be difficult to keep the DRY principles when developing Hudson plugins. Looking at what all plugins do, though, there is a standard pattern for most Publishing plugins:

  • Determine where the information to be published is to be found
  • Process/extract the information on the slave
  • Send the persistent result of the build to the master
  • Finalize processing of the information on the master
Strictly speaking only one of the last two steps is required, however, in most cases there is a trade-off between tasks that are more performant when executed on the slave and tasks that must be performed on the master as either the required information is not available on the slave or the activity will affect the object instances on the master as an artifact of the serialization call-by-value that occurs on the slave.

Thus to keep the DRY principles we need to:

  • Publisher or MavenReporter constructs a Configuration object.
  • If the Configuration requires tasks to execute on the slave, ensure that it is sent to the slave and execute those tasks on the slave.
  • If the Configuration requires tasks to execute on the master, send it (if necessary back) to the master and execute those tasks on the master.
There are other issues with respect to Actions, i.e. Actions for a "m2 project type" must extend a specific subclass. However, this is not a problem, as there is nothing to stop us just having all the actions extend this type, the non-"m2 project type" usage will just ignore that it's extending the subclass.

What's next

In part 3, I will present a couple of wrapper classes that we can use to ensure that the DRY principle gives us plugin execution in one place. Part 4 will tie these wrapper classes into the Hudson plugin framework, giving us a MavenReporter and a Publisher that both use common code to deliver the plugin functionality. Part 5 will add aggregated reporting for m2 project. Part 6 will add simple trend graphs. Part 7 will close out by actually parsing the JavaNCSS files and implementing our plugin!

2 comments:

Kaj Kandler said...

Stephen,
this tutorial is very interesting. However, is this difference between m2 and free-style projects still relevant in 2013? Have the APIs changed so that this is taken care of inside of Jenkins?

Thanks in advance!

Kaj

Stephen Connolly said...

The difference is still there, but the "standard" APIs are now better supported by the Maven2 project type making the need to use the "special" Maven2 project type way less pressing.

The only real reason to use the Maven2 way is to access the configuration of the plugin from the pom.xml... which can be very convenient for users, but makes the plugin author's life a living hell