Form validation

Often you want to check the correctness of the values entered by the user on the fly. This is called "form validation", and Jenkins performs this on the server side.

You write a form validation logic as a method on your Descriptor class, and it’ll look something like this:

public FormValidation doCheckThreads(@QueryParameter String value) throws IOException, ServletException {
  try {
    Integer.parseInt(value);
    return FormValidation.ok();
  } catch (NumberFormatException e) {
    return FormValidation.error("Not a number");
  }
}

The name of the method follows the convention doCheckXyz where xyz is the name of the field you put in your view. The method gets invoked in response to the onchange event on HTML DOM. So the above check method should have a corresponding view like this:

<f:entry title="Number of Threads" field="threads">
  <f:textbox />
</f:entry>

The parameter name value is also significant. The throws clause isn’t. If you want to use a different name as variable, e.g. meaningfulName you can do so, with:

public FormValidation doCheckThreads(@QueryParameter("value") final String meaningfulName) throws IOException, ServletException {
  // ...
}

This naming convention is how Jenkins wires up your form validation method. The return type also must be FormValidation, which offers various static factory methods to create an instance. Form validation can be of 3 different kinds: ok, warning or error. Validation message can either be represented in plaintext or use HTML markup. Several validations of different kinds can be reported wrapping then into one using aggregate factory method.

Setting up form validation within a Jelly template

To configure form validation within a Jelly template to call doCheckXxx methods, defined in a particular class in Jenkins, you need to utilize the checkUrl and checkDependsOn attributes.

Here’s a breakdown of how these attributes are used:

The checkUrl attribute specifies the URL that Jenkins requests to perform form validation. checkUrl is either a URL or an expression that produces a URL. If accompanied by checkDependsOn, it’s interpreted as a URL. This mode, as we interpret it, is then considered "modern". If checkDependsOn is missing, checkUrl is interpreted to be in the legacy mode by using the unsafe eval browser feature. The value entered in the input field is checked via AJAX against this URL, and any validation errors encountered are rendered under the text field. This URL is associated with the method responsible for performing the validation (doCheckXxx method) within your Descriptor class.

In the modern checkUrl mode, which as of Jenkins 2.360 requires the checkDependsOn attribute to be set (even if it is an empty string), automatically adds the value of the current form element as the query parameter called value. It specifies the field(s) to be sent and includes, but is not limited to, name and value. When the input field(s) is changed, Jenkins triggers the validation of the current field. Additionally, the fields specified in checkDependsOn are sent along with the form validation request.

Getting values of other fields

Sometimes your validation method needs values of other input fields to perform a check. You do this by defining them as additional parameters:

public FormValidation doCheckThreads(@QueryParameter String value, @QueryParameter String cpu) {
  try {
    int t = Integer.parseInt(value);
    int c = Integer.parseInt(cpu);
    if (t*c>10) {
      return FormValidation.warning("That's asking for too much");
    } else {
      return FormValidation.ok();
    }
  } catch (NumberFormatException e) {
    return FormValidation.error("Not a number");
  }
}
<f:entry title="Number of Threads" field="threads">
  <f:textbox />
</f:entry>
<f:entry title="Number of CPUs" field="cpu">
  <f:textbox />
</f:entry>

The parameter names are used to match up the Java method parameters to the form values. (So you could have used the parameter names "threads" instead of "value" — "value" is a reserved keyword that designates the "this input control")

When multiple parameters are defined like this, the validation kicks in whenever one of the dependent values is changed.

Relative path

Used in conjunction with QueryParameter to refer to nearby parameters that belong to different parents. ".." refers to values in the parent object, and "foo" refers to the child object of the current object named "foo". They can be combined with '/' like path, such as "../foo/bar", "../..", and etc. A good way to think about this is the file system structure. @RelativePath is like the dirname, and QueryParameter is like the basename. Together they form a relative path. And because of the structured form submissions, form elements are organized in a tree structure of JSON objects, which is akin to directories and files. The relative path then points from the current input element (for which you are doing form validation, for example) to the target input element that you want to obtain the value.

public FormValidation doCheckThreads(
    @QueryParameter String value,
    @RelativePath("../../parent")
    @QueryParameter String cpu
) {
  try {
    int t = Integer.parseInt(value);
    int c = Integer.parseInt(cpu);
    if (t*c>10) {
      return FormValidation.warning("That's asking for too much");
    } else {
      return FormValidation.ok();
    }
  } catch (NumberFormatException e) {
    return FormValidation.error("Not a number");
  }
}

Accessing context

Sometimes you want to access the context. For example, you might want to access the current FreeStyleProject object while validating a field of a Builder. You do this by putting AncestorInPath annotation.

public FormValidation doCheckThreads(@QueryParameter String value, @AncestorInPath AbstractProject project) {
  ...
}

References