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
pom.xml
src/main/java
src/main/resources
src/main/webapp
Suppose you have a java class stored in src/main/java/org/myorganization/MyAction.java that you would like to define Jelly files for.
- Create folder under resources with the same name as the class
- Create src/main/resources/org/myorganization/MyAction/
- 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:
- Create a java file
- Let's use src/main/java/org/myorg/MyAction.java
- Define a method in the class
Let's define
public String getMyString() { return "Hello Jenkins!"; }
- 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"> ${it.myString} </j:jelly>
- 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 ofJenkins
(orHudson
)instance
- an object currently being configured, within a section of a configure page such as aBuildStep
;null
if this is a newly added instance rather than reconfiguringdescriptor
- theDescriptor
object (see below) corresponding to the class ofinstance
h
- an instance ofhudson.Functions
, with various useful functions
Predefined URLs
Pages can use the following variables to create absolute links:
rootURL
- the Jenkins instanceresURL
- static webapp resources such as JavaScript or HTML (cf.Jenkins.RESOURCE_PATH
)imagesURL
- likeresURL
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:
- 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. - 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. - 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 thegetPort
method or a publicport
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> <f:entry title="${%Host}" field="host"> <f:textbox /> </f:entry> </j:jelly>
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 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> <f:entry title="${%Host}" field="host"> <f:textbox default="${descriptor.defaultHost()}/> </f:entry> </j:jelly>
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).
- 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.
- 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. - In your
configure
method, call save(). This will save the serializable fields of your plugin class. - In the
start
method of your plugin class, call load(), which will reload these fields from persistent storage on startup. 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}"/> </f:entry> </f:section> </j:jelly>
We use
it.port
to refer to the current value of the parameterport
(note thatconfig.jelly
for a plugin class usesit
instead ofinstance
). In the help attribute, we use the artifactId you specified when you created your plugin (seepom.xml
in the root directory of your plugin if you forgot what your artifactId is).- Store the file
config.jelly
insrc/main/resources/path/to/plugin/class/config.jelly
. For instance, if your plugin class ishudson.plugins.exampleplugin.MyPlugin
, put the file insrc/main/resources/hudson/plugins/exampleplugin/MyPlugin/config.jelly
. Create a file called
help-projectConfig.html
containing an HTML fragment like this:<div> <p> Your help text goes here. </p> </div>
Store it in
src/main/webapp/help-projectConfig.html
.- 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
- Plugin Structure
- Stapler URL mapping rules
- Jelly taglib reference core, define, stapler, and taglibs defined in Jenkins core
- UI Samples Plugin