On one of my projects at work we have moved to JIRA 3 (beta). We moved to benefit from the new custom
workflow feature. Unfortunately it was missing one key feature we wanted: the ability to send notification
emails on custom workflow transitions (I've just been told by Atlassian that this is a feature they're
currently working on). To remedy this and thanks to Atlassian's support, I've decided to delve
in the JIRA Java API and develop a workflow function plugin to implement email sending.
I have to say that JIRA 3's extensibility is great! JIRA can almost be seen as a full fledge foundation
for developing project tracking applications, in the same spirit as Eclipse is a full fledge
foundation for developing java applications (RCP). Both Eclipse and JIRA come with a default
application using this API to demonstrate their power (the IDE for Eclipse, the issue tracker for JIRA).
Note that the
new plugin system in JIRA has several similiarities with the Eclipse plugin architecture. Of course, I'm sure
JIRA still has a lot of ground to cover to expose a plugin API covering all domains of issue tracking (i.e.
allowing to replace all parts of the JIRA issue tracker) but it's going in the right direction.
The source code is available here and
the plugin jar is available here.
Setting up the project
Here's the directory structure I have chosen for my plugin. Please also note that I have used Maven to
perform the build (extremely easy to setup as Atlassian is also using Maven and they have all their jars
in a Maven remote repository on http://repository.atlassian.com).
A plugin is composed of several files (it is packaged as a JAR at runtime):
A plugin descriptor (atlassian-plugin.xml
)
Java source files
Velocity templates for the plugin UI (the *.vm
files)
The project.properties
file simply adds the Atlassian Maven remote repo to the list of repos
searched by Maven to download dependencies:
Generating the plugin jar is as simple as typing maven jar
.
The Worflow Function plugin extension point
Here's what the atlassian-plugin.xml
plugin descriptor contains:
Plugin for sending emails on custom workflow transitions.1.0Sends a notification email.sendmail.jira.plugin.workflow.SendMailFunctionfalsetruetrue900false
What you have to understand:
A plugin is made of 2 java classes: a plugin factory class (SendMailFunctionPluginFactory
) which is in charge of setting up all
that is necessary for the execution of the plugin feature, and the plugin execution class (SendMailFunction
).
A workflow plugin is expected to bundle 2 velocity template files: one for asking the user to input some data required by the plugin execution (this is
the input-parametes
velocity template, and one for displaying what the function will do. The later is visible if you click on a workflow
transition in JIRA and then on the post-functions tab.
The Java API
The Plugin Factory class
Without further ado, here's the skeleton for the SendMailFunctionPluginFactory
class:
The constructor is called when you click on the "add" button to add the function to your list of post-functions. The FieldManager
instance can be
used to get issue fields meta-data (it does not contain any issue data as there's no issue associated with the function yet - This will only happen when the function is
triggered by an issue transition).
The getVelocityParamsForInput()
method can be used to store some properties in the velocityParams
map. These properties will then be
accessible from the "input-parameters"
Velocity template.
The getVelocityParamsForView()
method can be used to store some properties in the velocityParams
map. These properties will then be
accessible from the "view"
Velocity template. In addition the descriptor
parameter provides access to the data entered by the user in the input phase (these data are stored in the workflow data structure itself).
The getDescriptorParams()
method is the bridge between the data contained in the Velocity context and the data in the Workflow context. More precisely
you put in there the code to extract the data that have been entered by the user in the Velocity context and you put the data in the workflow descriptor context. This descriptor
context is the second parameter that is available in your getVelocityParamsForView()
method.
Here's a look at the input-parameters
velocity template:
Group emails:
Comma-separated list of JIRA groups to send emails to.
Individual emails:
Comma-separated list of JIRA users to send emails to.
As you can see, the variables groupEmails
and individualEmails
will hold the data entered by the user.
The Plugin Function class
Here's the code that implements the plugin feature (in our case the sending of the notification email):
The execute()
method is called by JIRA when an issue transition happens.
The parameters have the following meanings:
The transientVars
parameter holds useful data such as the issue that was modified. You get a referernce to the issue by calling transientVars.get("issue");
. It contains
also other piece of data such as the comment
entered by the user, etc.
The args
parameter holds all the data stored in the workflow context (aka the workflow descriptor). This is the data you have stored yourself in the getDescriptorParams()
method explained above.
I'm not too sure what the ps
parameter is used for. I think it holds data related to the workflow steps but this needs to be confirmed. Anyway, you shouldn't need it in most cases.
Deploying and executing the plugin
Deployment is a simple as dropping the plugin jar in [jira install dir]/atlassian-jira/WEB-INF/lib
for example (it may be possible to drop it in other classloaders but I haven't tried).
In the following image we can see how JIRA has automatically discovered our plugin and extracted information from the plugin descriptor to make them available at the right extension point in JIRA:
Here is the page (using the input-parameters
Velocity template) to let the user enter data for our function:
Here is how our function is displayed (using the view
Velocity template):
I hope you got a good feel of what's possible to do with the JIRA API.
Note: For those wondering, I am not affiliated with Atlassian at all. I simply happen to like their tools (JIRA, Confluence) and I like the spirit of their team.