Jenkins Plugin Implementation – Part 2

Total: 7 Average: 4.4

In part two of the article, we will look at the remaining stages of Jenkins plugin implementation.
If you haven’t read the first part yet, please feel free to check it out before reading further.

Implementing the business logic (back-end)

Let’s look at how the business logic of a Jenkins plugin can be implemented by using the Build step implementation as an example. The approach will be similar for other step types (pre-build, post-build, publisher).

To implement any plugin that creates a Build step on the output, we will need to first implement a class that inherits the Builder abstract class. Builder encapsulates the basic logic of any build step.

public class MyStepBuilder extends Builder {

When implementing a plugin, the UI configuration part (Jelly file) and the Java file are directly related, so all required plugin parameters should be set via a constructor. To do this, mark the constructor with the @DataBoundConstructor annotation and pass all required parameters to it as arguments.

private final String param1, param2;
public MyStepBuilder (String param1, String param2) 
  this.param1 = param1;
  this.param2 = param2;

Next, we’ll need to define the getters for the declared fields. This is done to make sure that the UI can get the necessary values in the case of us modifying an already created step configuration.

public String getParam1() 
  return param1;

Optional parameters are defined via the setters. For the UI to have access to them, they should be marked with the @DataBoundSetter annotation.

public void setOptionalParam (String optionalParam) {
  this.optionalParam = optionalParam;

Now, let’s look at an implementation of the perform method. The build step actions are executed directly within it:

public boolean perform(AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener) 
	// some code

In this method, we have access to:

  • build- the entity that provides us with the build settings and various info about it.

  • launcher – the Jenkins executable environment entity.

  • listener – the entity that allows us to access the logger and control the build results.

We should also note a few additional redefinition methods that may prove useful. For example, the prebuild method can be used to provide additional environment validation before building. The getRequiredMonitorService() method is used for synchronization with other builds in the scope of the task. This can be useful in the process of integration with internal tools that don’t support parallel use. This method can return one of the following values:

  • BuildStepMonitor.BUILD – if the step requires the absence of unfinished builds

  • BuildStepMonitor.STEP – if the step requires the absence of similar unfinished steps in other builds

  • BuildStepMonitor.NONE – if synchronization is not required

Implementing validation

In addition to validating the environment before building, the prebuild method allows you to validate parameter values as they are being set. This is achieved by implementing a descriptor inside of your Builder class. The descriptor should implement the BuildStepDescriptor abstract class, and the @Extension annotation is used to connect it with the Builder class. For example:

public static final class DescriptorImpl extends BuildStepDescriptor<Builder> {

Inside, you can set additional parameters for your step by redefining the methods. For example, DisplayText and Help can be set like this:

public String getDisplayName() 
    return "Name for our plugin";
public String getHelpFile() 
    return "/plugin/MyJenkinsPlugin/resources/io/jenkins/plugins/sample/help-myplugin.html";

To validate a specific field with the help of a descriptor, you will need to create a public method with the following signature:

FormValidation doCheck[FieldNameInCamelCase](@QueryParameter String value)

Inside, by implementing the logic of checking a specific field’s value, we can achieve validation directly at the moment when the user configures the step. For example:

public FormValidation doCheckParam1(@QueryParameter String value) 
  if (value.length() == 0)
    return FormValidation.error(Messages.MyBuilder_DescrImpl_errors_missingParam1());
  return FormValidation.ok();

This particular implementation of the doCheck method will lead to a warning message if the user leaves the param1 field empty. You won’t need to write any additional code in the Jelly file – Jenkins will connect the fields with the validator on its own.

Also, the descriptor contains many other methods (load, save, configure etc.). Redefining them can be helpful when implementing the build settings validation on the configuration stage.

JUnit test coverage

When you’re writing Jenkins plugins, it is implied that all developed steps will be extensively covered by JUnit tests. An example of such tests is provided with the sample plugin by Maven.

Two entities/moqs that are very useful in test coverage:

  • JenkinsRule – moq of the Jenkins executable environment

  • FreeStyleProject – moq of the Jenkins project

When using them, it is quite easy to test if the connection between the Jelly file and Java is configured properly:

public JenkinsRule jenkins = new JenkinsRule();
private final String param1 = "param1", param2 = "param2";
public void testConfigRoundtrip() throws Exception 
  FreeStyleProject project = jenkins.createFreeStyleProject();
  project.getBuildersList().add(new MyStepBuilder(param1, param2));
  project = jenkins.configRoundtrip(project);
  MyStepBuilder myStepBuilder = new MyStepBuilder(param1, param2);
  jenkins.assertEqualDataBoundBeans(myStepBuilder, project.getBuildersList().get(0));

If the binding is incorrect, then the two following results are possible:
• An exception will occur (for example, the constructor accepts a different set of parameters)
• The object’s end state will not correspond to the reference state

Artem Kravets
Latest posts by Artem Kravets (see all)

Artem Kravets

Artem Kravets is a software development team leader at Devart with a bachelor's degree in Computer Engineering and extensive .NET knowledge. He is a ICAgile Certified Professional (ICP) with good experience in .NET desktop development, add-in integration, development of Maven plugins in Java and implementation of CI/CD processes.