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
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.
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!
Stephen,
ReplyDeletethis 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
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.
ReplyDeleteThe 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