J Tricks - Little JIRA Tricks
 
Sending emails from a Java application is not rocket science! But why bother if JIRA has its own APIs to do the job for
you?

I am talking about taking advantage of the issue event management in JIRA.

We all know about the notification schemes in JIRA and how JIRA sends email to the various subscribers when an event is fired. All we need to do is to make use of this feature.

Following are the simple configuration steps needed.



  1. Define a template which renders the subject and body of the email message - both html and text version. I have discussed about this in detail in my book. In case if you missed it, check out the email-template-id-mappings. xml file under WEB-INF/classes folder. I have created the following entry:

    <templatemapping id="99">
      <name>Mail Servlet</name>
      <template>mailservlet.vm</template>
      <templatetype>issueevent</templatetype>
    <
    /templatemapping>

    Note that you can do this programmatically when the plugin is 
    enabled but I am not going into those details here.
  2. Write the templates defined above. You need 3 templates, all with the same name mailservlet.vm (name defined above).

    Subject : The subject file goes into WEB-INF/classes/templates/email/subject folder

    Subject will be something simple. We are going to keep it very simple!

    Test Mail from Servlet

    html : The html file goes into WEB-INF/classes/templates/email/html folder

    This can be as fancy as you want. Let us keep it simple, using just 2 velocity params.

    Hello there!
    <br><br>
    #set ($baseUrl = $params.get("baseurl")) Are you having fun reading the <a 
    href="http://www.j-tricks.com">j-tricks</a> tutorial? Take a look at <a href="$baseUrl/browse/$issue.key">$issue.key</a>.
    <br><br>
    $params.get("sender").getDisplayName()


    As you can see, there are 2 velocity variables, $params and $issue, that we have used in this template. We will see soon 
    where those comes from.

    text : The text file goes into WEB-INF/classes/templates/email/text folder

    Again, let us keep it simple as shown:

    Hello there!

    Are you having fun reading the j-tricks (http://www.j-tricks.com) tutorial? Take a look at $issue.key.

    $params.get("sender").getDisplayName()


  3. Restart your JIRA so that the template mapping is effective. No need to do this if you are handling it programmatically when the plugin is enabled.
  4. Create a custom event and map it to the new template we defined.
  5. Add the new event to the notification scheme and add subscribers to this event notification.

Now that the template is defined and the event added, we can write the code that feeds the relevant context for the template and then fires the event. In your plugin, all you needed is a few lines of code.

  1. Create the context needed for the velocity template used by the event. For an issue event, a large number of variables are already available: https://developer.atlassian.com/display/JIRADEV/Contents+of+the+Velocity+Context.

    You can see both issue and params, the two velocity variables we used, a part of the list. params here will be populated by us when the event is fired.

    A map of variables that will be added to params is created as follows.

    Map<String, Object> context = new HashMap<String, Object>();
    context.put("sender", loggedInUser);
    context.put("baseurl",ComponentAccessor.getApplicationProperties().getString(APKeys.JIRA_BASEURL));


    We are adding just sender and baseurl as those are the 2 ones taken from params in the template above.
  2. Fire the event. Here 10000L is the id of the event to be fired. We use the custom event that is mapped to our velocity template but the same mechanism can be sued to fire system events as well.

    issueEventManager.dispatchEvent(10000L, issue, context, loggedInUser, true);

That's it! The event is fired and mail will now be sent to all the subscribers as defined in the notification scheme.

How about sending it only to selected people and not depending on the notification schemes? Like the share feature in
JIRA5? i.e. send only to selected users? Replace the dispatchEvent with the following.

  1. Create the map of params as in the above case.
  2. For each user, create a notification recipient and add it into a Set of recipients.

    User user = ComponentAccessor.getUserUtil().getUser("jobin");
    NotificationRecipient recipient = new NotificationRecipient(user);
    HashSet<NotificationRecipient> recipients = new HashSet<NotificationRecipient>();
    recipients.add(recipient);


  3. Create an IssueEvent object with our issue, context and the event Id.

    IssueEvent event = new IssueEvent(issue, context, loggedInUser, 10000L);

  4. Create a MailQueueItem using this event, recipient list and the template id. Here 99 is the template id.

    MailQueueItem item = issueMailQueueItemFactory.getIssueMailQueueItem(event, 99L, recipients, "");

    Note that you will have to enable to jira-core dependency in the pom.xml for IssueMailQueueItemFactory.
  5. Add the item to the mail queue.

    ComponentAccessor.getMailQueue().addItem(item);

And that's all. The mail queue will take care of dispatching our message.
I have attached the code of a simple servlet plugin which does both the approaches at the end of this page. After installing the plugin, it can be tested by accessing the servlet. Use a valid issue key and a receiver attribute if you are not using notification scheme.

Example calls:

http://localhost:8080/plugins/servlet/mailservlet?receiver=jobinkk&issue=DEMO-1
http://localhost:8080/plugins/servlet/mailservlet?issue=DEMO-1



Have a good day!
send-mail.zip
File Size: 15 kb
File Type: zip
Download File

 


Comments

07/10/2012 10:31am

Jobin,

A handy article, as ever. It looks like the email is sent by the mail queue, which has a 60s interval, right?

Also, a useful trick I found for adding more variables to the standard Velocity context for regular email templates is to define a calculated custom field type that always returns the same thing - a Map of the objects you want in the context. Then in the email velocity template
retrieve the value of the custom field with $customFieldManager.getCustomFieldObjectByName()

~Matt

Reply
J-Tricks
07/10/2012 11:07am

Matt, yes. It will be sent by the mail queue every N minutes as defined in the MailQueueService.

Nice trick about adding the objects to velocity context. Never thought about that before :)

Reply
07/23/2012 3:49am

It is quite interesting to know this kind of emails sending process and will try the plugins you mention in the post .

Reply
Bastien
11/23/2012 8:06am

Hi !

I've tried this code, and i have this exeception :

ERROR ServiceRunner Mail Queue Service [atlassian.jira.mail.IssueMailQueueItem] This code or velocity template expects a GenericValue, but received an Issue. We need to recode
java.lang.UnsupportedOperationException: This code or velocity template expects a GenericValue, but received an Issue. We need to recode
at com.atlassian.jira.issue.DocumentIssueImpl.getLong(DocumentIssueImpl.java:446)
at com.atlassian.jira.mail.IssueMailQueueItem.send(IssueMailQueueItem.java:143)
at com.atlassian.mail.queue.MailQueueImpl.sendBuffer(MailQueueImpl.java:66)
at com.atlassian.jira.service.services.mail.MailQueueService.run(MailQueueService.java:28)
at com.atlassian.jira.service.JiraServiceContainerImpl.run(JiraServiceContainerImpl.java:61)
at com.atlassian.jira.service.ServiceRunner.execute(ServiceRunner.java:47)
at org.quartz.core.JobRunShell.run(JobRunShell.java:195)
at com.atlassian.multitenant.quartz.MultiTenantThreadPool$MultiTenantRunnable.run(MultiTenantThreadPool.java:72)
at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:520)


Here is a part of my sendMail method code :

String subject = issue.getSummary();

Map<String, Object> context = new HashMap<String, Object>();
context.put("sender", userManager.getUserObject("admin"));
context.put("baseurl",ComponentAccessor.getApplicationProperties().getString(APKeys.JIRA_BASEURL));
context.put("summary", subject);
issueEventManager.dispatchEvent((long) 10023, issue, context, userManager.getUserObject("admin"), true);


Is there a problem with my issue object "issue" in the method ?

Reply
J-Tricks
11/23/2012 12:56pm

Which version of JIRA is it?

Reply
Bastien
11/26/2012 12:30am

My Jira version is 5.0.7

Bastien
11/26/2012 3:15am

I found a solution to my problem :

The dispatchEvent() method is exepecting a MutableIssue, and not an Issue.
So, i map my issue to a MutableIssue :

MutableIssue i = issueManager.getIssueObject(issue.getKey());

That's it !


Thank you for your tutorial :)

Reply
J-Tricks
11/26/2012 7:35am

Great :)

Reply
12/13/2012 2:49am

does the job perfectly. thanks again.

Reply
12/14/2012 2:41am

also is there a way to do all the process of creating a template, notification scheme and add subscribers in the plugin?
like just install the plugin and you are ready to send email like:
http://localhost:8080/plugins/servlet/mailservlet?issue=DEMO-1

Reply
J-Tricks
12/14/2012 5:47am

You can do most of it when the plugin is enabled. See http://www.j-tricks.com/1/post/2012/03/plugin-lifecycle-events.html for details.

You will have to manually add the template though!

Reply
Shabbir K
02/12/2013 5:50am

HI

I am trying to create a screen where user can select 'groups' from group picker and have an option to send email notification (containing the ISSUE details) to the selected groups only. Is it possible with this plugin, please advise

Reply
J-Tricks
02/12/2013 6:53am

You can try the method where you add recipients and add the mail item.

Reply
Matt Doar
02/14/2013 4:24pm

Shabbir, why not just define a notification scheme that uses a Custom Group Picker field. Then have the user populate the custom field and email will be sent to the groups in that field.

Reply
roark
03/25/2013 6:29am

How do i include the package com.atlassian. jira in my project

Reply
J-Tricks
03/25/2013 7:47am

Do you have the jira-api dependancy in the pom.xml? That will bring in most classes. Some of them are still part of core API and so you will have to add jira-core dependancy.

<dependency>
<groupId>com.atlassian.jira</groupId>
<artifactId>jira-core</artifactId>
<version>${jira.version}</version>
<scope>provided</scope>
</dependency>

Reply

Your comment will be posted after it is approved.


Leave a Reply


J tricks - Little JIRA Tricks