in java jira custom_plugin sendemail github ~ read.

How to write a custom JIRA plugin that will send email on custom field value change

So, as i described here it was decided to write a custom plugin that will acomplish concrete goal for our need :

  • We have custom fields that has various values
  • We need to send e-mail to specific people when custom field was modified with specific value
  • Email should be sent only if :
    • Ticket was just created and this value was already set to needed one
    • Someone was editing the issue and changed the value of the custom field to needed one
  • If someone edit's issue, but do not change the custom field - e-mail should not be sent

The goal is clear, our environment is ready for development and the final part is to actually write the code and make jar file that we will add to our jira instance.
This plugin consist of 3 main classes and one properties file:

  • Email Triggers - this class checks our custom fields values and also verifies that change into the custom field was performed. If changed value for custom field is needed one, then we create an email and add it to mail queue through MailSender class
package com.biercoff.listener.plugin;

import com.atlassian.jira.event.issue.IssueEvent;
import com.atlassian.jira.issue.CustomFieldManager;
import com.atlassian.jira.issue.Issue;
import com.atlassian.jira.issue.fields.CustomField;
import org.ofbiz.core.entity.GenericEntityException;
import org.ofbiz.core.entity.GenericValue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Iterator;
import java.util.List;
import java.util.Properties;

public class EmailTriggers {
    private static final Logger log =
    private MailSender mail;
    private Properties props;

    public EmailTriggers() {
        mail = new MailSender();

    public void checkCustomFieldOnCreate(CustomFieldManager customFieldManager,
    Issue issue) {
        CustomField customField = customFieldManager.
        getCustomFieldObjectByName("{Custom Field}");
        String value = customField.getValue(issue).toString();
        if (value.equals("Value")) {
            log.debug("{Custom field name} is needed");
            String subject = "{Custom field name } is required for " 
            + issue.getKey();
            String body = getMessageBody(issue);
            subject, body, "{Custom field name}");

    public void checkCustomFieldOnUpdate(IssueEvent issueEvent,
    Issue issue) throws GenericEntityException {
        if (fieldWasChanged("${custom field name}}", 
        issueEvent, "${value that will trigger email}}")) {
            log.debug("Field was changed");
            String subject = "${Field name} is might be needed for " +
            String body = getMessageBody(issue);
            subject, body, "{custom field change}");

    protected boolean fieldWasChanged(String fieldName, 
    IssueEvent issueEvent, String fieldValue) 
    throws GenericEntityException {
        boolean result = false;

        List<GenericValue> changeItemList =

        Iterator<GenericValue> changeItemListIterator =
        while (changeItemListIterator.hasNext()) {
            GenericValue changeItem = (GenericValue)
            String currentFieldName = changeItem.get("field").toString();
            if (currentFieldName.equals(fieldName)) // Name of custom field.
                Object oldValue = changeItem.get("oldstring");
                Object newValue = changeItem.get("newstring");
                if (oldValue!= null && newValue != null){
                    if (!oldValue.equals(newValue) &&
                    newValue.equals(fieldValue)) result = true;
                }else if (oldValue == null && newValue != null) {
                    if (newValue.equals(fieldValue)) result = true;
        return result;

    protected String getMessageBody(Issue issue) {
        return "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0
        Transitional//EN\" \"
        -transitional.dtd\">\n" +
                "<html xmlns=\"\">\n" +
                "<head>\n" +
                "<body>\n" +
                "<h1>Issue details</h1>\n" +
                "<li>Summary:\t " + issue.getSummary() + "</li>\n" +
                "<li>URL:\t http://localhost:2990/jira/browse/" +
                issue.getKey() + "</li>\n" +
                "<li>Assignee:\t " + issue.getAssignee() + "</li>\n" +
                "<li>Reporter:\t " + issue.getReporter() + "</li>\n" +
                "<li>Description:\n</li> " + issue.getDescription() + "\n" +
                "</body>\n" +

    protected void loadProperties()  {
        props = new Properties();
        try {
            InputStream input = 
        }catch (IOException e){

  • Mail Sender class - this class is responsible for generating email and adding it to the mail queue
package com.biercoff.listener.plugin;

import com.atlassian.jira.component.ComponentAccessor;
import com.atlassian.mail.Email;
import com.atlassian.mail.MailFactory;
import com.atlassian.mail.queue.SingleMailQueueItem;
import com.atlassian.mail.server.SMTPMailServer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class MailSender {
    private static final Logger log = 

    public void sendEmail(String to, String subject, String body, String emailType) {
        SMTPMailServer mailServer = 
        Email email = new Email(to);
        SingleMailQueueItem item = new SingleMailQueueItem(email);
        log.debug(emailType + " email was added to the queue ");
  • Custom Field Listener - this class is responsible for catching events on every issue in JIRA and apply logic for custom field change verification.
import com.atlassian.event.api.EventListener;
import com.atlassian.event.api.EventPublisher;
import com.atlassian.jira.event.issue.IssueEvent;
import com.atlassian.jira.event.type.EventType;
import com.atlassian.jira.issue.CustomFieldManager;
import com.atlassian.jira.issue.Issue;
import org.ofbiz.core.entity.GenericEntityException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;

public class CustomFieldListener implements InitializingBean,
DisposableBean {
    private static final Logger log =
    private final CustomFieldManager customFieldManager;
    private final EventPublisher eventPublisher;
    private final MailSender mail;
    private final EmailTriggers triggers;

     * Constructor.
     * @param customFieldManager
     * @param eventPublisher injected {@code EventPublisher} implementation.
    public CustomFieldListener(CustomFieldManager customFieldManager,
    EventPublisher eventPublisher) {
        this.customFieldManager = customFieldManager;
        this.eventPublisher = eventPublisher;
        triggers = new EmailTriggers();
        mail = new MailSender();

     * Called when the plugin has been enabled.
     * @throws Exception
    public void afterPropertiesSet() throws Exception {
        // register ourselves with the EventPublisher

     * Called when the plugin is being disabled or removed.
     * @throws Exception
    public void destroy() throws Exception {
        // unregister ourselves with the EventPublisher

     * Receives any {@code IssueEvent}s sent by JIRA.
     * @param issueEvent the IssueEvent passed to us
    public void onIssueEvent(IssueEvent issueEvent) throws GenericEntityException {
        Long eventTypeId = issueEvent.getEventTypeId();
        Issue issue = issueEvent.getIssue();

        if (eventTypeId.equals(EventType.ISSUE_CREATED_ID)) {
            triggers.checkCustomFieldOnCreate(customFieldManager, issue);
        } else if (eventTypeId.equals(EventType.ISSUE_UPDATED_ID)) {
            triggers.checkCustomFieldOnUpdate(issueEvent, issue);


To make your jira instanse send actually emails you need to update maven-jira-plugin in pom.xml file in a next way :

					<!-- Uncomment to install TestKit backdoor in JIRA. -->

To debug your code and see how it works, you need to do this simple steps :

  • In Intelij IDEA click Run -> Edit Configuration
  • Create new Remote 'JIRA' configuration and set next fields :
    • Host: localhost
    • Port 5005
  • In the IDEA's terminal run atlas-debug command
  • Wait for project to build and start up.
  • When it's ready, click Run -> Debug 'JIRA'

It should connect to your running jira project, which you can access with localhost:2990/jira URL. When you add or change something in the code, you need to recompile and redeploy your plugin. You don't need to restart atlas-debug command -

  • Open another terminal window
  • Go to the project root folder and run atlas-cli command.
  • When it will finish booting up , run pi command - it will redeploy your plugin and then you can check your changes.

I have put a dummy project on github so you could check it out if you'll face with some problems, or if you just want to use it right away.

comments powered by Disqus
comments powered by Disqus