Child pages
  • Basic guide to Jelly usage in Jenkins
Skip to end of metadata
Go to start of metadata

If you are unfamiliar with Jelly and Jenkins' use of it, you may find it difficult to create configurable parameters for your plugin. This page describes some very simple examples to get you started.

UI Samples

The first thing you should do is install the UI Samples Plugin in your test instance so you can see how to do some common things.
It is available on the update center.

Loading Your First *.jelly

A basic plugin structure is 


Suppose you have a java class stored in src/main/java/org/myorganization/ that you would like to define Jelly files for.

  1. Create folder under resources with the same name as the class
    • Create src/main/resources/org/myorganization/MyAction/
  2. Add index.jelly to that folder
    • Write your first jelly file in src/main/resources/org/myorganization/MyAction/index.jelly

Now you can start getting more complex!

Understanding the it object

As the above section hints at, Jelly files are tied directly to classes. This means they can call methods on those classes. To reference the file they are tied to, jelly files use the it keyword. To define code, use the dollar sign and curly-braces, like this: "${insert code here}".

Here is a simple example:

  1. Create a java file
    • Let's use src/main/java/org/myorg/
  2. Define a method in the class
    • Let's define

      public String getMyString() {
          return "Hello Jenkins!";
  3. Write a jelly file with the following
    • <j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout"
               xmlns:t="/lib/hudson" xmlns:f="/lib/form">
  4. Load the class, and note that 'Hello Jenkins!' is displayed

A few things to notice: the 'get' was automatically stripped from the method name, and the first letter of the remaining method name was lower-cased. I'd recommend using the Java convention for naming methods (e.g. starting getters with 'get' and using CamelCase) so that Jelly can always find the methods.

Other predefined objects

Depending on the page being rendered, other objects besides it may be predefined:

  • app - the instance of Jenkins (or Hudson)
  • instance - an object currently being configured, within a section of a configure page such as a BuildStep; null if this is a newly added instance rather than reconfiguring
  • descriptor - the Descriptor object (see below) corresponding to the class of instance
  • h - an instance of hudson.Functions, with various useful functions

Predefined URLs

Pages can use the following variables to create absolute links:

  • rootURL - the Jenkins instance
  • resURL - static webapp resources such as JavaScript or HTML (cf. Jenkins.RESOURCE_PATH)
  • imagesURL - like resURL but with /images appended, for loading icons and the like

(Use resURL rather than rootURL wherever possible as it permits browser caching, improving responsiveness and lessening server load.)

These URLs are defined as soon as you are within an l:layout or an l:ajax block. This means that on any Jenkins page you already have them available except if your page is loaded via ajax. Then you should add the l:ajax tag.

Note that until 1.505 rootURL will be empty in the typical case of Jenkins running in development mode with no context path (http://localhost:8080/); it is used for creating server-absolute paths such as /some/path for direct links. As of 1.505 it will be /jenkins in test mode, to help remind you to use it! In the rare case that you need to pass a complete URL to an external service for some reason, use app.rootUrl instead (which takes into account “Jenkins URL” from the system /configure screen). Hyperlinks in HTML has a fuller treatment of this topic.

Iteratively Modifying *.jelly files

It's worth mentioning that in most cases you don't need to re-start the Jenkins server, simply modify the *.jelly file in your editor of choice and re-request the page that will load that jelly. 

Objects with Descriptors

For objects with Descriptor (such as Publisher), the general steps are as follows:

  1. Define a immutable class that takes all the configuration parameters as constructor parameters. Put @DataBoundConstructor on this constructor, which tells Jenkins how to instantiate it.
  2. Define getters for the configuration fields, or make the fields "public final". This allows Jelly script to read the values to populate the configuration page.
  3. Write a Jelly fragment (normally named config.jelly but see javadoc of your base class) and list all the configuration options. The most basic form of this is something like the following. The value of @field is used as a property name (so you need to either have the getPort method or a public port field) as well as the constructor parameter name.
<j:jelly xmlns:j="jelly:core" xmlns:f="/lib/form">
  <f:entry title="${%Port}" field="port">
    <f:textbox />
  <f:entry title="${%Host}" field="host">
    <f:textbox />

That odd "${%...}" thing is a marker for internationalization.

Help files

If your plugin has a help-FIELD.html or help-FIELD.jelly in the src/main/resources/path/to/plugin/PluginName directory, Jenkins will put (question) icon and render your help inline. You can add top-level plugin help, e.g. on the job configuration page, by providing help.html or help.jelly in the src/main/resources/path/to/plugin/PluginName directory. If you add help for a field for a job configuration, e.g. a post-build action, add the help-FIELD.html file next to  your config.jelly file, e.g. to the src/main/resources/path/to/plugin/PluginName/YourPostBuildAction directory instead.

Form validation

You can write doCheckFIELD method on your descriptor to add the form validation logic. Your check method would look something like this:

public FormValidation doCheckPort(@QueryParameter String value) {
  if(looksOk(value))  return FormValidation.ok();
  else                return FormValidation.error("There's a problem here");

You can also define additional parameters with @QueryParameter to obtain values from other nearby form fields of the specified names. This is useful if your validation depends on values on the other form fields.

Refer to stapler documentation for more details.

Default value

If you want the configuration page to have the initial default value, use @default. The first example shows the literal default value, while the second example shows the programmatically computed default value:

<j:jelly xmlns:j="jelly:core" xmlns:f="/lib/form">
  <f:entry title="${%Port}" field="port">
    <f:textbox default="80" />
  <f:entry title="${%Host}" field="host">
    <f:textbox default="${descriptor.defaultHost()}/>
Other jelly variables

When using the field= mechanism shown above you see where the data really comes from. Jenkins defines these variables which you may use as needed: descriptor and instance, which are the instance of your Descriptor class and instance of the class it describes, respectively (both are available in config.jelly, just descriptor in global.jelly).

Objects without Descriptors

If your plugin uses an object without a Descriptor, such as ComputerListener, follow these steps to include a single text box in the configuration page that will be readable within your plugin class (the one that extends Plugin).

  1. Override the configure(StaplerRequest req, JSONObject formData) method in your plugin class. This method will be called when the user clicks the Save button in the configuration page.
  2. In your configure method, use a method like optInt to extract the value you would like the user to configure. For instance, formData.optInt("port", 3141) will get a port number if the user enters it, or the value 3141 if the user leaves it blank. Store the extracted value in a member of the plugin class.
  3. In your configure method, call save(). This will save the serializable fields of your plugin class.
  4. In the start method of your plugin class, call load(), which will reload these fields from persistent storage on startup.
  5. Create a file called config.jelly with content like this:

    <j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout"
             xmlns:t="/lib/hudson" xmlns:f="/lib/form">
      <f:section title="My Plugin">
        <f:entry title="${%Port}" help="/plugin/ARTIFACT_ID_GOES_HERE/help-projectConfig.html">
          <f:textbox name="port" value="${it.port}"/>

    We use it.port to refer to the current value of the parameter port (note that config.jelly for a plugin class uses it instead of instance). In the help attribute, we use the artifactId you specified when you created your plugin (see pom.xml in the root directory of your plugin if you forgot what your artifactId is).

  6. Store the file config.jelly in src/main/resources/path/to/plugin/class/config.jelly. For instance, if your plugin class is hudson.plugins.exampleplugin.MyPlugin, put the file in src/main/resources/hudson/plugins/exampleplugin/MyPlugin/config.jelly.
  7. Create a file called help-projectConfig.html containing an HTML fragment like this:

      Your help text goes here.

    Store it in src/main/webapp/help-projectConfig.html.

  8. Start Jenkins normally (using mvn hpi:run). Navigate to the configure page and observe your new section. Click the help link and observe your help text.

Useful links


  1. Unknown User (

    I'm trying to make my config.jelly or global.jelly file appear in the Global System Configuration from Hudson, but it never appear there. My plugin implements an TopLevelItemDescriptor. Am I doing something wrong? I mean, is it wrong to supose that an TopLevelItemDescriptor has an global.jelly file to configure it?

    What I actually want is an way to set some configurations for my project type. And that every new job uses that configurations. Is it possible?

  2. Hello,

    This might not be the right place to post this but I could not find any other. Anyway, I wonder if it is possible to do some AJAX dynamic form with jelly. I am developing a plugin that, in its configuration page, displays the user a dropdownlist. According to the value selected I need to change the content of another section of the page (a list of checkboxes actually). So basically I need to listen to onChange event or something like this. Does anyone have met the same need and found a solution? 


    1. Hi Hadrien,

      I am facing with the same requirement as you. Just go through the plugin development document. Will develop a plugin.I believe i can leverage the ui-samples plugin.

      what is your final solution?

      1. Hi, it goes a while back now but I remember doing some pretty ugly stuff: I basically coded javascript inside my jelly template. I don't think there is another way around but I would like to be proven wrong. 

        Overall, I don't really see why jenkins is using  jelly for front-end pages where such great frameworks are now available.

  3. Finally developed a dynamic-parameter plugin to solve my issue. 

    This plugin can read a file to create choice parameters dynamically. and further more, it can generate the options value at runtime:

  4. Hi Roject Luo,

    I have also same requirement on dynamic parameter plugin but some what different. Based on the other parameter it should read and generate choise parameters dynamically at runtime itself. Can you help on this one?

  5. I developed a configurable dynamical parameter plugin. It will generate choice parameter at runtime. Just trying to publish this to jenkins.

    same of conifg for parameters:

    <parameter name="Jenkins" value=""> <!- script depends on environment and application -> <!- script depends on application only -> <parameter name="Application" value="APP1"> <parameter name="Environment" value="ASM" /> <parameter name="Environment" value="SYS" /> <parameter name="Environment" value="PROD" /> </parameter> <parameter name="Application" value="APP2"> <parameter name="Environment" value="PROD" /> </parameter>
    <parameter name="Jenkins" value="">

    <!- script depends on application only ->

    <parameter name="Application" value="APP1">

    <parameter name="Environment" value="ASM" />

    <parameter name="Environment" value="SYS" />

    <parameter name="Environment" value="PROD" />


    <parameter name="Application" value="APP2">

    <parameter name="Environment" value="PROD" />



  6. Hi - I am trying to understand the difference between objects with descriptors vs ones without them.. is there any more documentation on this I could read. I am new to plugin development and just learning.


  7. hi,

    i am new to plugin development 

    i have written one new plugin but it is not working as expected

    can someone please review my code 


  8. I am trying to create a plugin for Websphere DataPower deployment. Whenever I run the command "mvn clean install -Pjenkins" it gives the below & I can see the .hpi file inside target folder. But when I install it, I can not see it in add build step or anywhere else (apart from the installed plugin tab). Can someone help me, what I am doing wrong here.

    c:\tools\plugin\datapower_soma>mvn clean install -Pjenkins
    [INFO] Scanning for projects...
    [INFO] ------------------------------------------------------------------------
    [INFO] Building TODO Plugin 1.0-SNAPSHOT
    [INFO] ------------------------------------------------------------------------
    [INFO] -- maven-clean-plugin:2.4.1:clean (default-clean) @ datapower_soma --
    [INFO] Deleting c:\tools\plugin\datapower_soma\target
    [INFO] --- maven-hpi-plugin:1.106:validate (default-validate) @ datapower_soma -
    [INFO] --- maven-enforcer-plugin:1.0.1:enforce (enforce-maven) @ datapower_soma
    [INFO] --- maven-enforcer-plugin:1.0.1:display-info (display-info) @ datapower_s
    oma ---
    [INFO] Maven Version: 3.3.3
    [INFO] JDK Version: 1.8.0_60 normalized as: 1.8.0-60
    [INFO] OS Info: Arch: amd64 Family: dos Name: windows 7 Version: 6.1
    [INFO] -- maven-localizer-plugin:1.14:generate (default) @ datapower_soma --
    [INFO] --- maven-resources-plugin:2.5:resources (default-resources) @ datapower_
    soma ---
    [debug] execute contextualize
    [INFO] Using 'UTF-8' encoding to copy filtered resources.
    [INFO] Copying 2 resources
    [INFO] --- maven-compiler-plugin:2.5:compile (default-compile) @ datapower_soma
    [INFO] Compiling 3 source files to c:\tools\plugin\datapower_soma\target\classes

    [INFO] --- access-modifier-checker:1.7:enforce (default-enforce) @ datapower_som
    a ---
    [INFO] --- maven-hpi-plugin:1.106:insert-test (default-insert-test) @ datapower_
    soma ---
    [INFO] --- gmaven-plugin:1.5-jenkins-1:generateTestStubs (test-in-groovy) @ data
    power_soma ---
    [INFO] No sources found for Java stub generation
    [INFO] --- maven-resources-plugin:2.5:testResources (default-testResources) @ da
    tapower_soma ---
    [debug] execute contextualize
    [INFO] Using 'UTF-8' encoding to copy filtered resources.
    [INFO] skip non existing resourceDirectory c:\tools\plugin\datapower_soma\src\te
    [INFO] --- maven-compiler-plugin:2.5:testCompile (default-testCompile) @ datapow
    er_soma ---
    [INFO] Compiling 1 source file to c:\tools\plugin\datapower_soma\target\test-cla
    [INFO] --- maven-hpi-plugin:1.106:test-hpl (default-test-hpl) @ datapower_soma -
    [INFO] Generating c:\tools\plugin\datapower_soma\target\test-classes\the.hpl
    [INFO] --- maven-hpi-plugin:1.106:resolve-test-dependencies (default-resolve-tes
    t-dependencies) @ datapower_soma ---
    [INFO] --- gmaven-plugin:1.5-jenkins-1:testCompile (test-in-groovy) @ datapower_
    soma ---
    [INFO] No sources found to compile
    [INFO] -- maven-surefire-plugin:2.16:test (default-test) @ datapower_soma --
    [INFO] Surefire report directory: c:\tools\plugin\datapower_soma\target\surefire

     T E S T S

     T E S T S
    Running InjectedTest

    Tests run: 4, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 82.971 sec - in

    Results :

    Tests run: 4, Failures: 0, Errors: 0, Skipped: 0

    [INFO] -- maven-license-plugin:1.7:process (default) @ datapower_soma --
    [INFO] Generated c:\tools\plugin\datapower_soma\target\datapower_soma\WEB-INF\li
    [INFO] -- maven-hpi-plugin:1.106:hpi (default-hpi) @ datapower_soma --
    _[INFO] Generating c:\tools\plugin\datapower_soma\target\datapower_soma\META-INF_
    [INFO] Building jar: c:\tools\plugin\datapower_soma\target\datapower_soma.jar
    [INFO] Exploding webapp...
    [INFO] Copy webapp webResources to c:\tools\plugin\datapower_soma\target\datapow
    [INFO] Assembling webapp datapower_soma in c:\tools\plugin\datapower_soma\target
    [INFO] Generating hpi c:\tools\plugin\datapower_soma\target\datapower_soma.hpi
    [INFO] Building jar: c:\tools\plugin\datapower_soma\target\datapower_soma.hpi
    [INFO] --- maven-install-plugin:2.3.1:install (default-install) @ datapower_soma
    [INFO] Installing c:\tools\plugin\datapower_soma\target\datapower_soma.hpi to C:
    [INFO] Installing c:\tools\plugin\datapower_soma\pom.xml to C:\Users\IBM_ADMIN\.

    [INFO] Installing c:\tools\plugin\datapower_soma\target\datapower_soma.jar to C:
    [INFO] ------------------------------------------------------------------------
    [INFO] ------------------------------------------------------------------------
    [INFO] Total time: 01:52 min
    [INFO] Finished at: 2015-10-08T11:24:38+01:00
    [INFO] Final Memory: 46M/373M
    [INFO] ------------------------------------------------------------------------

  9. Here is the pom.xml file I am using:

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="" xmlns:xsi=""

        <!- Baseline Jenkins version you use to build and test the plugin. Users must have this version or newer to run. ->

      <description>Continuous Integration for DataPower</description>
          <name>MIT License</name>
      <!-- If you want this to appear on the wiki page:
          <name>Bob Q. Hacker</name>
      <!-- Assuming you want to host on @jenkinsci:

    Unknown macro: {project.artifactId}



    Unknown macro: {project.artifactId}

      <!-- If you want to depend on other plugins:


Write a comment…