Built-in Listeners

Many of the post-functions are also available as listeners. This lets you have better control over your business processes. As an example, let’s say you need a cloned issue created in a different project whenever a Critical priority issue is raised.

Although you can put the Clones an issue and links post-function on an event, if the issue is created as Major priority then subsequently edited and changed to Critical, a workflow function won’t catch it. This is where listeners come in.

Listeners are accessed in the admin UI (find the Admin cog and then Add-ons), under the ScriptRunner section and Script Listeners. In addition to the settings available when installed as workflow functions, you can also specify which events and which projects are applicable.

You can filter on projects using the Condition field as well, however as projects are much more likely to be used with listeners this is made more explicit here.

If you need to filter on issue type you can do that in the condition field, eg:

issue.issueTypeObject.name == "Bug" // && any other conditions

Example Conditions are supplied which you can use as templates.

You can add multiple configurations for the same type of listener if they have different parameters.

The listeners are not documented here as they have the same functionality as their workflow function counterparts. The listeners currently available are:

Add Watcher

This listener will add the current user as a watcher to the issue. This is useful for instance if you want all commenters to be added as watchers.

It is similar to using the Participants field in the notification scheme, however using this listener allows people to remove themselves from the watchers.

For example, to add all commenters as watchers add the listener, select the project, and choose the Issue Commented event. You can further refine this by using a condition, for instance if you wanted to restrict this behaviour to just certain components or certain priorities of issue.

Version Synchroniser Listener

This listener allows create, update, delete, move, merge etc. operations on JIRA versions across multiple projects. The listener is useful if you have multiple projects that should all have the same versions list.

It should be used carefully as will modify and delete versions in all configured target projects, if that operation is done to the source version.

The following image shows an example version synchroniser:

version synchroniser
  1. The listener will listen for version-related events in the selected source project. For this listener, the event list is pre-populated and you cannot update it.

  2. You can select multiple target projects. The listener won’t allow you to select target projects that contain the source project, as that is not meaningful.

Please understand the functionality of the listener before implementing it in your production environment. The listener propagates the following:

  1. version creates to all target projects, when a version is created in the source project

  2. version updates, if the version is already available in the target project

  3. version deletes. It does not delete the target version if it is referenced in issues and the swap option is not defined for affected issues

  4. version merges, if the same versions are available in the target project. It does not trigger a merge operation if the merge-to version is unavailable in the target project

  5. move and reorder operations. The order of versions in target projects may not be the same as the source project, if the version names in the target project differ greatly

  6. release, and archive operations

At the current time, the release operation does not move unresolved issues in target projects, because the release event does not contain details of the version to move unresolved issues to.

Before doing a release, it is advisable to run a query across the project group that the listener is configured for (i.e. source and target projects) to check for unresolved versions:

project in (P1, P2, P3) and fixVersion = 1.0 and resolution is empty

You can then do a bulk update before releasing the version in the source project.

Custom Listeners

Using groovy scripts as workflow functions instead of full-blown java plugins is much faster, more maintainable and flexible, and has all the same capabilities as java plugins. Explicit compilation is much over-rated.

As well as listening for issue events, you can also handle project, version and user-related events. Examples of this might be:

  • mail a new user when they are created in JIRA

  • notify downstream systems when a version is released

  • create link git repositories or Confluence spaces when a project is created

There are a couple of ways to structure your listener code.

The easiest, and recommended way, is to just write a script. You can use an inline script, or point to a file in your script roots, as usual.

The event object will be passed to the script in the binding. For issue related events, this will be an IssueEvent. So the simplest listener might look like:

issue resolved event

For other types of listeners, the event type will be whatever you selected to listen to, for example a ProjectUpdatedEvent.

Currently, the static type checker is not smart enough to handle different event types, so if you are working with non-issue events, you will need to cast or redefine the event object to the correct type (or you can just ignore the type checker). For example in the following case, without redefining the event, the type checker would flag oldProject as an error:

project event

If you are listening to multiple events because the code is mostly common, you can distinguish between them using instanceof:

multiple events

Issue Event Bundles

SRJIRA-1929 has been fixed, which means your specified event handler will be correctly called for the relevant events.

JIRA 6.3.10 and above broadcasts "bundles" of events, containing all relevant events. ScriptRunner will unbundle the events, and call all relevant script listeners for each event.

Previous to this fix, if you had a handler listening for the Issue Assigned event, it would only have been invoked if the issue assignment was the only action made. If someone had updated or commented an issue at the same time as assigning it, the Issue Updated or Issue Commented event would have been published, and your issue assigned handler would not run.

After this fix, it will run, but the consequence is it will be called more often than before, and perhaps when you do not expect it to. For instance, if you create an issue and set the assignee at the same time, this will invoke any Issue Assigned listeners (where previously it would not).

We advise you to write your listeners in such a way that this makes sense…​ for instance, if you want to do something when the assignee changes, then either consider the initial assignee to be a change, or to check the previous value of that field.

Or, you can choose to ignore events that were raised alongside a more "significant" event. To do this, you can make use of the IssueEventBundle, which is available in the script binding as bundle.

Example:

import com.atlassian.jira.event.issue.DelegatingJiraIssueEvent
import com.atlassian.jira.event.issue.IssueEvent
import com.atlassian.jira.event.issue.IssueEventBundle
import com.atlassian.jira.event.type.EventType
        def getIncludedEvents = {
            bundle.events.findResults {includedEvent ->
                includedEvent instanceof DelegatingJiraIssueEvent ? includedEvent.asIssueEvent() :  null
            } as List<IssueEvent>
        }

Custom Listener Example

The following example will post a message to a cloud instant messaging provider when a version is released. The listener is configured for the VersionReleaseEvent.

import com.atlassian.jira.event.project.VersionReleaseEvent
import groovy.json.JsonBuilder
import groovyx.net.http.ContentType
import groovyx.net.http.HTTPBuilder
import groovyx.net.http.Method

VersionReleaseEvent event = event

def http = new HTTPBuilder("https://hooks.slack.com")
http.request(Method.POST, ContentType.TEXT) {
    uri.path = "/services/XXXX/YYYYY/ZZZZZ" (1)
    body = new JsonBuilder([
        channel   : "#dev",
        username  : "webhookbot",
        text      : "Woop!: Version: *${event.version.name}* " +
            "has been released in project *${event.version.projectObject.name}*. We're off to the pub!!", (2)
        icon_emoji: ":ghost:",
        mrkdwn    : true,
    ]).toString()
}
1 get this value from the Slack API documentation
2 get the version and project names from the event

"Heritage" Custom Listeners

This is the old method of using custom listeners. It still works, but you are not advised to use it for new code:

  1. Write your listener as groovy - but as a groovy class, not a groovy script. You could just extend AbstractIssueEventListener, but your class merely needs to respond to workflowEvent(IssueEvent).

  2. Copy the .groovy file to the correct place under the script roots directory (depending on its package).

  3. Go to Script Listeners, and enter the name of the package and class, e.g. com.onresolve.jira.groovy.listeners.ExampleListener. This is in the distribution so you could try this actual class, it just logs the events it catches.

If you update the groovy class it will be reloaded automatically

The simplest possible listener class looks like this:

class ExampleListener extends AbstractIssueEventListener {
    Category log = Category.getInstance(ExampleListener.class)

    @Override
    void workflowEvent(IssueEvent event) {
        log.debug "Event: ${event.getEventTypeId()} 
            fired for ${event.issue} and caught by ExampleListener"
    }
}

For how-to questions please ask on Atlassian Answers where there is a very active community. Adaptavist staff are also likely to respond there.

Ask a question about ScriptRunner for JIRA, for for Bitbucket Server, or for Confluence.