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.
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.
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(value); 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 value changes (modulo JENKINS-19124).
TODO: talk about @RelativePath
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) { ... }