Chapter 1. Plugin Basics


Building any plugin for JIRA starts with downloading the most recent Atlassian Plugin SDK (Software Development Kit) from the Atlassian Developers site at[1] This chapter explains how to use the Plugin SDK and some other things common to all plugins. The same SDK is also used to develop plugins for other Atlassian products such as Confluence. Subsequent chapters contain details about specific kinds of JIRA plugins.

If you haven’t already found them, there are a number of tutorials about developing different kinds of plugins online at I recommend using them and their source code at with this book since they mostly complement each other.

The Atlassian Answers site at is the best place to ask technical questions about how to develop plugins. The tag jira-development is most commonly used. The old JIRA Developer Forum at is also a useful place to find answers but it’s now read-only.

Beyond some basic Java skills, in order to write a JIRA plugin you’ll also need to understand how Apache Velocity templates are used by JIRA. These are the files that control how HTML pages are formatted, and are covered in more detail in Apache Velocity. For more complicated plugins you may need to know more about Apache Maven, the build and deploy tool for Java described in the section Maven.

JIRA uses many other technologies and it can be useful to have some familiarity with any of these: Subversion, Mercurial, Ant, Apache JSP, JavaScript, JQuery, Soy and Jersey for REST. However for most plugins you won’t need to know anything about them—just Java and a bit of Velocity and Maven.

Creating a JIRA Plugin

JIRA plugins use an Apache Maven 2 project for each plugin to produce a Java .jar file that can be installed in JIRA.

A Maven project has a fairly rigid layout, always with a top-level XML file named pom.xml made up of the plugin’s name, version, dependencies and so on. The groupId and artifactId variables in pom.xml are what make a plugin unique within Maven. The artifactId is also what will appear as part of your plugin .jar file’s name. The name variable is the more verbose name of the plugin, as seen in the JIRA plugin administration pages. The SDK will create a working pom.xml file for us with some reasonable default values.


Atlassian’s guidelines on the use of JIRA are that a name such as Acme plugin for JIRA® is okay, whereas JIRA® plugin for Acme is not. The former connotes a 3rd party plugin for JIRA®, while the latter connotes an Atlassian-developed tool. More information is available at or contact

The first thing to do is to unpack the SDK in a convenient location. This location is referred to as $SDK_HOME in the examples below. You can then create a JIRA plugin with a single command:


This command will prompt you for a few values and then create a new plugin directory with enough files to build a plugin. This skeleton plugin does nothing, but it can be installed in JIRA and will appear in the list of plugins shown in the Administration plugins page.

As mentioned earlier, the artifactId and groupId values appear directly in the Maven pom.xml file. Every Atlassian plugin has a unique identifier named key. The key for each plugin is defined in the file atlassian-plugin.xml, and by default the key is artifactId.groupId.

The version argument is the version of your plugin, not the JIRA version. Versions can be any text but the Atlassian Marketplace (see the section Atlassian Marketplace) now requires that the version starts with three sets of digits, not two. A version can also look like 5.1.0-alpha. The package argument is the name of the Java package that the plugin’s Java source code will use.


If you want to, you can also provide the same information that was required to create a plugin directly from the command line:

$SDK_HOME/bin/atlas-create-jira-plugin \
  --artifactId myplugin \
  --groupId com.mycompany.jira.plugins \
  --version 5.1.0 \
  --package com.mycompany.jira.plugins.myplugin 

Important Plugin Files

The most important files in a plugin directory and their purposes are as follows.


The top-level Maven 2 project file for the plugin.


The place to start for the description of what the plugin is intended to do. This can be renamed to anything you like or removed.


A place holder for the license for the plugin. Many JIRA plugins use the BSD license by default, but the section Further Reading has more information about choosing a license.


The directory that contains all the source files needed to build the plugin.


The XML file that specifies exactly what the plugin really contains. The order of the elements in this file doesn’t make a difference to how they work.


The default location for Java source files, based on the package name used when the plugin was created.


A sample Java source file, automatically generated. You can rename or delete this file later on.


The location of all generated files after a build has finished. None of the files in this directory need to be version controlled.


The generated plugin package that is deployed to JIRA to actually use the plugin. The filename contains the version that was specified in pom.xml.

There are a few other files generated in the src/test directory related to testing your plugin. You can delete them if they’re not wanted, but an even better practice is to write some tests as described at

Reading a Plugin

Before diving into building a plugin, it’s useful to know how to understand what any JIRA plugin does. Knowing how to read a plugin also makes it easier to find examples on which to base your own work.

My own approach is to start with the top-level Maven file pom.xml and to look for the jira.version element. That should tell you which version of JIRA the plugin was last built against. If it’s too old for you to use, then see Updating a Plugin Version at the Marketplace for ideas on how to update it. There is also a element that tells you the version of the JIRA database schema the plugin last worked with. As might be expected, this changes less frequently than jira.version but should be close to the value of jira.version.

The next file to read is src/main/resources/atlassian-plugin.xml. This file contains all the different plugin module types that are used in the plugin and tells you what the plugin provides; e.g., a new custom field type. The initial skeleton plugin doesn’t have any plugin modules in this file because it doesn’t do anything.

Then I’ll read any documentation or perhaps look at a few screen shots. Usually only after all that do I look at actual Java source code and Velocity template files.

Building and Deploying a Plugin

The minimal commands to build a plugin from the command line are:

$ cd myplugin
$ $SDK_HOME/bin/atlas-package
[INFO] ----------------------------------------------------
[INFO] ----------------------------------------------------
[INFO] Total time: 26 seconds
[INFO] Finished at: Wed May 11 03:11:48 PDT 2011
[INFO] Final Memory: 56M/118M
[INFO] ----------------------------------------------------

This last command will download the dependencies listed in the pom.xml, which includes the large atlassian-jira-N.N.N.jar for the version of JIRA specified in jira.version in pom.xml. Before spending time downloading these large files, it’s worth checking that the jira.version is set to the version of JIRA you really want to use.

The command will then compile the Java source files and create the plugin’s .jar file. You should make sure that you have a recent Java JDK installed on the machine where the plugin is being built. JIRA 4.1 supports the Oracle (formerly Sun) JDK 1.5, but JIRA 4.2 and later require JDK 1.6. JIRA 5.1 does not support JDK 1.7.

The first time all this is done it can take a while but subsequently should be faster. If the dependencies in your pom.xml file don’t change, then you can even add the -o or --offline flag to atlas-package to work offline and make the build a little faster.

When the build is finished you should see a .jar file in the target directory. That’s your plugin .jar file.

To deploy the plugin to a production JIRA instance, copy the plugin .jar file to plugins/installed-plugins in the JIRA_HOME directory—or with JIRA 4.3 and later, you can upload it using AdministrationPlugins and the Install or Manage Plugins tab. Prior to JIRA 4.4, most JIRA plugins require you to restart JIRA to complete a plugin installation or update.

Using atlas-run and atlas-cli

The SDK also provides another way to build and deploy a JIRA plugin. If you type:


instead of atlas-package, then a brand new instance of JIRA is automatically configured in the target directory. Then the plugin .jar file is deployed there and the new JIRA instance is started up at http://localhost:2990/jira. You can log in with the user name admin and password admin and test your plugin directly there.

This second approach takes a little longer to start up the first time, but you don’t have to configure anything such as a license. The other benefit of this approach is that in some cases it allows for much faster redeployment of plugins. If you open another separate window from the same top-level directory and type:


then a maven2> prompt appears. Typing pi for plugin install at the prompt will rebuild and redeploy your plugin in a few seconds instead of the minute or so it can take to restart JIRA. More information about all of the SDK commands can be found at

This is a fast way to develop plugins, but prior to JIRA 4.4, it only works if a plugin is using certain plugin module types. The list of which types do and do not work with pi can be found at The good news is that as of JIRA 4.4 almost all plugins can be reloaded using pi or from the Administration menu, which will save everyone a lot of time during development.

The fastest way to develop a plugin now is to use the FastDev tool ( After starting JIRA using the atlas-cli command this tool checks each time that a page is reloaded whether the plugin needs to be rebuilt and also reloaded.

What Can JIRA Plugins Do?

Doing something useful in your plugin involves adding one or more plugin modules to the atlassian-plugin.xml file. You’ll also most likely need to add some Java source and Velocity template files. Chapter 2 and the rest of this book covers exactly how to do that.

There are over 30 different types of plugin modules listed at Each plugin module has a separate page documenting the XML attributes and elements that it supports.


Much of JIRA’s own functionality is defined using these same plugin module types in files with names that look like system-*-plugin.xml. These source code files are always a good place to check for working examples of each plugin module type. The complete list of JIRA’s plugin module types is configured in the file

The different plugin modules that are documented at can be grouped as follows.

Custom Fields


Provides a new type of custom field for JIRA, as described in Chapter 2.


Provides a new searcher for custom fields, described further in Chapter 4.



Adds a new workflow condition to restrict when the status of an issue is changed. There is an example in Conditions.


Adds a new workflow condition to validate the inputs when the status of an issue can be changed. There is an example in Validators.


Adds a new workflow post-function to update an issue after its status has changed. There is an example of this in Post-Functions.

User Interface


These plugin modules let you add new tabs to the Project page, a project’s Components page, a project’s Versions page and to the view screen of an issue.


Add new operations or groups of links to existing JIRA web pages and their menus. Since JIRA 5.0 the renderer module gives you more control over how the content of different parts of JIRA web pages are displayed.


Add a new keyboard shortcut to JIRA.


Change how an remote issue link is displayed. New in JIRA 5.0.


Add a new URL and web page to JIRA. This is used as part of the example in Adding Configuration to a Custom Field.


Prior to JIRA 4.0 this added a new operation to the screen that is shown when viewing an issue. Since replaced by the web-item plugin module



A new gadget that can be added to a JIRA dashboard.


A report that can be selected from a projects Project page.


Add a new JIRA Query Language (JQL) function. JQL is mentioned briefly in How Searchers Work.


Search Request views are the choices of how to view the list of issues in the Issue Navigator; e.g., Printable, Word or Excel.


This has been deprecated since JIRA 4.0, when it was replaced by gadgets. It was removed in JIRA 5.0.

Remote Access


Define a new REST endpoint. REST is the future direction for remote access to JIRA.


Add a new SOAP endpoint for JIRA remote access. SOAP is currently the most common way of accessing JIRA remotely.


Add a new XML-RPC endpoint for remote access. This method of accessing JIRA is less frequently used.

Other Plugin Module Types

Some plugin module types are common to all Atlassian plugins. Most of these are documented further at


These module types refer to other files and resources used by JIRA plugins. Examples include JavaScript and CSS files, image files and internationalization files (Internationalization). Since JIRA 5.0 the transformer module gives you more control over how these files are used in a plugin at runtime.


These modules are how JIRA defines which classes can be injected into the constructors of the Java classes used within a plugin. They have nothing to do with a project’s components.


These modules allow you to intercept calls to JIRA URLs and change what is returned, as well as using new Java servlets.


This module lets you define a listener as part of an Atlassian plugin. More information can be found at Since JIRA 5.0.


This module lets you extend the JIRA plugin framework by defining new module types.


These let you change how a user’s details appear in their profile.


This undocumented module controls what appears at the bottom of every page in JIRA. An example of its use can be found in the JIRA source file system-footer-plugin.xml.

Which Methods Should My Plugin Use?

A public Java API was introduced in JIRA 5.0 ( The classes and methods in the public API should not change as rapidly in each minor release. So if your plugin only uses the public API you shouldn’t have to update or recompile. A plugin built for JIRA 5.1 should just work when JIRA 5.2 is released. Obviously this promises to save everyone a lot of work. The rest of the core JIRA API is still available for plugins to use, but with the warning that it changes more rapidly that the public API.

To encourage plugin developers to use the public API, Atlassian have created a tool where you can upload a plugin’s .jar file and see a report about which parts of the plugin are not using the public API for a particular version of JIRA. More information about the plugin checkup tool see

The official policy about what will and will not change in the different kinds of releases can be found at

Troubleshooting a Build

In theory building your plugin should just work. In practice you may have one of the following common errors.[2]

If the build fails with the error Cannot execute mojo: resources then check that you’re in the same directory as the pom.xml file.

If you get errors about missing dependencies it means that Maven didn’t know how to download a particular file that was specified in a dependency element in pom.xml. Check for typos in the groupId and artifactId elements of the dependency, particularly if you added them by hand. You may also want to check for newer versions of the same dependency using Google. A few files such as activation.jar are not available from a Maven repository due to licensing restrictions, so they have to be downloaded and installed locally if needed.

Maven nicely provides you with the necessary command to install a .jar file locally to resolve a missing dependency. If you do this, then don’t forget to note where the .jar file was originally downloaded from.

Finding the correct version number for a Maven dependency is not always easy. Sometimes the version can be found by searching for an artifactId value in the pom.xml files within the JIRA source for the appropriate version of JIRA, or by looking at the version of the different .jar files that are shipped with JIRA. Since JIRA 5.0 the Atlassian Developer Toolbox has a page that shows the component versions being used in an instance of JIRA.


One useful tip is that any flags after an SDK command are passed directly to Maven. So you can add a parameter such as --file other-pom.xml to any atlas- command to try debugging a build by using an alternative pom.xml file.

Sometimes a build fails when it is building or running unit tests. Those tests are there for a good reason, but if you really have to build without them then you can disable them. Adding the parameter -Dmaven.test.skip to $SDK_HOME/bin/atlas-package prevents Maven from compiling and executing the tests. Or you can skip just their execution with the -DskipTests parameter.

It’s a good idea to avoid using the com.atlassian.jira namespace for your plugin’s Java source code. The classes that you create should be only visible to your plugin at runtime, but it’s confusing for other people trying to understand your code and is not recommended.

Another thing to be careful about is making sure that you only have one instance of a plugin deployed. Multiple versions of a plugin can accidentally get deployed if you change the plugin’s version number and then forget to remove the old plugin .jar file when you deploy the new one. Or if you have deployed a plugin both manually and using JIRA’s own plugin manager. Which of the two .jar files is used by JIRA is not defined and unexpected results may occur.

One of the most common problems with JIRA plugins happens at runtime. The log file contains an UnsatisfiedDependencyException error about a constructor in your plugin. This and other runtime problems are described in more detail at.

Another runtime error that can be hard to debug is using the wrong version of JIRA in pom.xml. If one particular JIRA URL produces a page with Java reflection errors about a class that changed subtly between versions of JIRA, then you may have compiled your plugin against one version of JIRA and be using it with another incompatible version. The surest sign of this is usually that there are no errors in JIRA log file. Check that the jira.version in pom.xml matches your local JIRA instance, delete the plugin’s target directory and rebuild and redeploy the plugin.


JIRA uses the Apache log4j logging framework to record messages in the log/atlassian-jira.log file under the jira.home directory. This is one easy way to understand what is happening within a plugin during its development. Another way is to use a debugger, as described in Using a Debugger with JIRA.

To use logging within a plugin, first add a Logger variable to your source code as shown in Example 1-1. The name of the Logger, in this case com.mycompany.jira.plugins.conditions.MyClass, can be any unique text but the full Java class name is often a convenient string.

Example 1-1. Example of logging in a plugin
package com.mycompany.jira.plugins.conditions;

import org.apache.log4j.Logger;

public class MyClass {

    private static final Logger log = Logger.getLogger(MyClass.class);

    public void MyMethod() {
        log.debug("This message is only seen at the DEBUG log level");


Next add the following two lines to the file atlassian-jira/WEB-INF/classes/ under the JIRA install directory: = DEBUG, console, filelog = false

The two lines in have a few parameters. The first one is the log level to use (DEBUG). The other two (console and filelog) are the names of appenders, which define the places that logging output goes. filelog is the appender for atlassian-jira.log. The second line with additivity stops any parent loggers from logging the same message more than once so you don’t see duplicate log entries.

The log level is the level of messages that you want to display. In this case we’re using the DEBUG log level. A log level of WARN would only show messages created with log.error() or log.warn(), but not or log.debug().

The most common log levels are in order: ERROR, WARN, INFO and DEBUG. If your plugin is using FATAL log messages, you’re doing something that is at risk of stopping all of JIRA, which seems a bit much for a mere plugin. There is also the less common TRACE level which is lower than DEBUG.

Restart JIRA to pick up the changes in and check the atlassian-jira.log file for output lines such as:

2010-12-31 15:23:41,123 http-9990-Processor24 DEBUG admin 55421x4x1 
129mqbi http://localhost:8800/test.jspa [jira.plugins.conditions.MyClass] 
This message is only seen at the DEBUG log level


If you aren’t seeing the log messages you expect to see, check the precise spelling of the appropriate entry in Also check that the log level of the message is equal or greater than the current level in

Once the file has had the new entries added, they can be temporarily changed at AdministrationSystemLogging & Profiling. In this case, the next time JIRA is restarted the log level will be set back to the value in Since JIRA 5.0 you can add new entries without restarting JIRA.

Another useful idea is to use the same identifier for all the log objects in a plugin across different classes. You can do this by using the same MyClass.class identifier in all the classes. Another approach to do the same thing is to declare the log variable as public in one class and the refer to it as a static variable from the other classes.

The entire log4j framework is described in much more detail at and

Apache Velocity

Apache Velocity is the templating language used by most JIRA plugins. A templating language allows you to use template files that contain most of the HTML that you want to appear on a web page, and then insert other pieces of HTML dynamically when the template is rendered into HTML by the JIRA server and is returned to a web browser. A plugin’s Java source files and its Velocity templates are usually closely related to each other.

Example 1-2 shows a simple example of a Velocity template. If it is stored in a file for a plugin it would usually have a .vm suffix and be located somewhere under src/main/resources/templates. Note that Velocity template filenames have to be unique within JIRA, not just within the plugin.

Example 1-2. example.vm—a simple Velocity template
    ## A simple Velocity template file
    #if ($value)
        This field is empty.

Velocity commands and user-defined macros start with a # character. Comments start with ## and Velocity variables begin with a $ character. Velocity files are rendered together with a Velocity context (basically a Java Map) that contains the Java objects that each Velocity variable refers to. These objects can be used like any ordinary Java object. For example, Example 1-2 shows the toString method being called on the value object. The rest of the file is used unchanged in the generated HTML, including any leading spaces and HTML comments.

If you use a variable such as $myvalue that is not yet defined, Velocity will display the string $myvalue in the HTML, and may log an error in the JIRA log file. If you’d prefer such an undefined variable to be rendered as an empty string then just use a quiet reference to the variable: $!myvalue. A quiet reference looks like a Java negation, but they are unrelated.

You can also access a property such as color of a variable with $myvalue.color. This is the same as using $myvalue.getColor() but is more compact.

You will also see references to a variable named i18n in many Velocity template files. This is how text messages are internationalized and is described further in Internationalization.

Debugging Velocity template files can be a fiddly task if you’re not sure what variables are available for use in the Velocity context. To make this task easier, some of the Velocity contexts used by JIRA add the Velocity context itself as a variable to the context. This let’s you display all the variables in the context with Velocity code such as:

  #foreach($variableName in $ctx.keySet())
      $variableName.toString() - $ctx.get($variableName).getClass().getName()<br/>

which will produce a listing of all the variables and their Java classes such as this:

ctx - java.util.HashMap
customFieldManager - com.atlassian.jira.issue.managers.DefaultCustomFieldManager
issue - com.atlassian.jira.issue.IssueImpl 
field - com.atlassian.jira.issue.fields.CommentSystemField 

In email templates the context is available as $context, and for some plugin types it is available as $ctx. However, the context is not available for custom field types.

There is some more information about the different Velocity contexts that are used by JIRA at There is a fairly concise Velocity User Guide available at and which I recommend reading.


If you are developing Velocity templates that are not delivered in a plugin .jar file, then you can edit to have JIRA reload all the .vm files without restarting. This setting and various other changes can also be enabled using the flag to JIRA, as described in JIRA Development Mode.

Velocity macros can be defined using the #macro command inline in the same .vm template file, but they can’t be shared between the various template files of a plugin.

Including one template file in another can be done with the #parse command. The name of the file to pass to the #parse command is everything below the src/main/resources directory, for example:


The #macro and #parse commands can be combined to make Velocity templates much easier to debug and maintain.


As introduced in Creating a JIRA Plugin, Apache Maven is a build and deploy tool used by JIRA and many other applications written in Java. This section contains more information about how Maven is used by JIRA plugins.

Maven downloads all the .jar files needed to compile and deploy a plugin using the dependency elements listed in the top-level pom.xml file. For example, if a plugin needs to access the HttpServletRequest variable in a method, you will need to add a dependency such as:


This tells Maven to search the repositories that it has been configured with for a group of releases named javax.servlet, and then search in those for the file named servlet-api-2.5.jar.

The part of dependencies that can be confusing if you’re not aware of how it works is the scope element. If there is no scope, then the .jar file is only available during compilation, but the .jar will also be packaged up in the plugin’s .jar file. A scope of provided means that the plugin can expect to find it as part of the files shipped with JIRA application, and so the .jar file is not added to the plugin .jar file. There are also other scopes such as test that have their own restrictions on when the classes in the .jar file are available.

The Plugin SDK that is used to build JIRA plugins is actually a series of atlas- scripts that call a local copy of Maven at $SDK_HOME/apache-maven/bin/mvn with a settings.xml file that already contains the locations of Atlassian’s own Maven repositories. If you’re already comfortable with Maven then you can modify your existing Maven installation with those settings and use it to build JIRA plugins instead of the SDK.

More information about Apache Maven can be found at Most Maven behavior is controlled by plugins chosen from the ones listed at For example, plugin unit tests use the Surefire plugin (

Further Reading

Software licensing is a complex matter. One place to start is with the list of different license types shown at Another helpful resource is the book Understanding Open Source and Free Software Licensing by Andrew M. St. Laurent (O’Reilly).

For the specific details of using different IDEs to create JIRA plugins, see

Dan Rosen of Atlassian wrote an excellent four-part blog post about creating Atlassian plugins that starts at html series covers creating a cross-product plugin but contains other useful information as well. The order of the posts is significant: IV, V, VI and I (think Star Wars).

Another well-written starter tutorial for a JIRA plugin that adds new menus and links is

[1] This SDK is not the same as the old JIRA Plugin Development Kit, which was used for JIRA 3.x plugins.

[2] In theory, practice is the same as theory. In practice, it differs. — Yogi Berra

Get Practical JIRA Plugins now with O’Reilly online learning.

O’Reilly members experience live online training, plus books, videos, and digital content from 200+ publishers.