Many people have come to know and love JIRA as an issue tracker for software development projects. What many people don’t realise is that JIRA, with its customisable workflows, is useful for any team that needs to track issues.
For example, within Atlassian, we use JIRA to:
- Track support requests from our customers;
- Track candidates in our recruitment process;
- Track the tasks and issues that arise in any significant project, such as our recent move, or our acquisition of Cenqua.
For last week’s ShipIt, Atlassian’s semi-regular day and a half of (almost) unconstrained innovation, I saw an opportunity for a plug-in that I hope will make JIRA even more appealing for applications outside of JIRA’s traditional domain.
Even in the age of e-mail and the web, for many businesses, the telephone remains a primary means of contact with their customers. Many of these phone calls require some follow up action, others are enquiries about the status of an existing issue.
Why not improve JIRA’s abilities as a tool for capturing and responding to telephone enquiries? Most calls include Caller ID, showing the called party the caller’s telephone number. Why not use this information to automatically retrieve the caller’s information in JIRA, saving time and frustration for both parties?
This is exactly what I set out to do.
JIRA Caller ID is a plug-in that integrates JIRA with an organisation’s telephone system, to allow one-click navigation to a caller’s issues, and more.
The plug-in places an “Identify Caller” icon in the toolbar that appears at the top of every JIRA page. When a JIRA user receives a phone call, they click the icon.
The first time a call is received from a particular number, the JIRA user sees the page depicted below. As you can see, JIRA has obtained the telephone number of the caller from the telephone system. From here, the JIRA user can look up the JIRA account of the caller, and associate it with the telephone number.
Thanks to the AJAX user picker that first appeared in JIRA 3.10, finding users is quick and easy. For more advanced search capabilities, you can open JIRA’s full user picker window.
Once the association between user account and phone number is created, JIRA navigates to the caller’s user profile. You can see the user’s telephone number is stored as a property of the user profile, named “CALLERID”.
This was as much as I’d hoped to deliver for my first ShipIt, but I found myself with a couple of hours to spare. Treading very carefully (I’d heard battle stories from colleagues who’d broken their ShipIt entries in the final frenzied rush to add just one more feature), I decided to add a call history feature, complete with recordings of answered calls:
Behind The Curtain
So how was it all done?
[By all means, skip this part unless you’re interested in the technical nitty-gritty!]
First step was to set up a phone system to integrate with. Any business of a significant size uses a PABX to manage its telephone calls. Traditionally, these have been based on very expensive proprietary hardware and software. In recent years, the Asterisk project has gained a lot of attention as an attractive alternative. It’s open-source, highly configurable and programmable, runs on a variety of inexpensive computing platforms, and integrates nicely with both traditional telephony hardware and lines, and the newer breed of VoIP devices and services. I downloaded and built Asterisk, and within 15 minutes had my own little telephone exchange running on my MacBook Pro.
Next step was to set up a telephone extension on my exchange. Luckily, Atlassian is already running a VoIP-based phone system (It’s actually running on a commercial derivative of Asterisk), so I was able to borrow a spare VoIP handset. Configuring it was a simple matter of adding some account details to Asterisk’s SIP protocol configuration file, and entering the same credentials on the handset. Once this was done, the handset registered with the server, and I was able to test my setup by dialling the various demonstration applications that come pre-configured with Asterisk.
What I needed next was a telephone number that could be used to dial in. I already had my telephone line at home integrated with an Asterisk server by means of a Digium TDM400P PCI card with an X100M FXO Module. It’s configured to forward my calls to my SIP-enabled mobile phone, when it’s in Wi-Fi coverage, to the softphone on my Mac Pro when I’m at work, or to Asterisk-hosted voicemail when I’m unavailable. After some minor reconfiguration, it was also forwarding my calls to my ShipIt Asterisk server at Atlassian. This required changes in three places:
- Adding account details for my ShipIt server to my home server’s IAX protocol configuration file, so that my ShipIt server becomes a “peer” of my home server, able to accept calls from it;
- Adding corresponding details, and an instruction to register with my home server, to my ShipIt server’s IAX configuration file. This makes my home server a “user” of my ShipIt server, able to place calls to it;
- Modifying the “dial plan”, the logic that determines call routing, on my home server so that calls into my home phone are routed to the new “user” on the ShipIt server. This is done by editing the extensions configuration file.
You may have noticed that I’m using two different protocols: SIP (Session Initiation Protocol) for signalling between Asterisk and the handset, but IAX (Inter-Asterisk eXchange protocol) between my two Asterisk servers. SIP is much more widely supported, and is the only protocol supported by the handset I was using. With a bit of work, I could have used SIP for both connections, but IAX has a couple of advantages that make it a much easier protocol to work with when firewalls are involved:
- SIP includes IP addresses in the payload of its messages, not just the IP header. This means that if you’re using NAT (Network Address Translation) to share one or more public Internet IP addresses between a group of hosts on a private network, your NAT implementation needs to be SIP-aware so that it can translate the addresses in both the header AND the payload. IAX does not include IP addresses in its payloads, so it doesn’t suffer from this problem.
- SIP only carries the signalling used to establish and control calls, and actually relies on other protocols, typically a combination of RTP (Real-time Transport Protocol) and RTCP (Real-time Transport Control Protocol), to carry the audio data. While SIP typically operates on a well-known port number (5060), RTP and RTCP typically use dynamically assigned port numbers. This makes getting SIP-based connections through NAT a real headache. IAX uses a single well-known port number (4569) for signalling and data, so it doesn’t suffer from this problem.
Now that I was able to call my home number and have my ShipIt extension ring, it was time to integrate Asterisk with JIRA.
JIRA plug-ins are simply JAR files containing an XML-based descriptor of the modules they contain, and Java classes and resources implementing the modules. The JIRA Caller ID plug-in includes the following modules:
|Module||Description||JIRA Plug-in Module Type|
|Identify Caller Link||The telephone icon link that appears in the top of every JIRA page||web-item|
|Identify Caller Action||A WebWork action that is executed whenever the telephone icon is clicked||webwork1|
|Caller ID Service||A component that implements the business logic of retrieving the Caller ID, and managing its association with JIRA user accounts||component|
Asterisk provides a few different integration points to choose from. I made use of the Asterisk Manager API, a socket-based interface that can be used to monitor and control activities on the Asterisk server. Integrating with this interface was easy, thanks to the open-source Asterisk-Java library.
With all this in place, the plug-in can query Asterisk for a summary of all active “channels”. A typical call consists of two channels (or “legs”): A channel between the originator of the call and Asterisk, and another channel between Asterisk and the recipient of the call. From the summary, the plug-in can determine the Asterisk-assigned names of any channels currently established to the demonstration handset.
The next step is to obtain the Caller ID. Every channel managed by Asterisk has a set of associated variables, which contain all kinds of useful information. For example, there is a built-in variable, CALLERID(num), that contains the telephone number from the Caller ID on the channel. The catch, however, is that this is the number of the party directly connected to this channel, but we need the number of the party on the other channel in the call. This easiest way to do this is to add a step to Asterisk’s dial plan to set a variable (_JIRA_CALLERID) on the inbound channel to the current Caller ID. Because the variable name starts with an underscore, Asterisk propagates it to the outbound channel when it is created. It is then available for the JIRA Caller ID to query, via the Asterisk Manager API.
Adding call recording was deceptively simple: It just took a couple of additional lines in the Asterisk dial-plan to generate a unique file name, make it available as a channel variable (so that the plug-in can query for it), and start recording the call.
Add a puff of smoke, a couple of strategically-placed mirrors, and there you have it: JIRA Caller ID, ShipIt edition.
Where To From Here?
Now that the mad rush of ShipIt is over, there’s a bit of work to be done to make it production ready: Hard-coded values to be made configurable, edge cases to be handled (such as phone numbers shared by multiple users, and users with multiple phone numbers), security to be added, automated tests to be written, and chewing gum and sticky tape to be removed.
Once that’s done, I’m keen to unleash it on support.atlassian.com, so that our legendary support team can put it to use (Donna, in our San Francisco support team, has already lodged a feature request!).
From there, I hope to package it as a plug-in, and make it available for download from our plug-in library. I’m also considering the possibility of providing an API for adding support for telephone systems other than Asterisk.
I’d like to say a quick thank you to a few of my colleagues: Cheers to Stephen on our UI team, for the cool little telephone icon he designed, and to our legendary SysAdmin, Steve, for lending me the VoIP handset. Thanks to the JIRA team for building such a great, flexible product to build upon. Finally, thanks to our management team for giving us the time to do ShipIt, and to fellow developer Dylan for organising ShipIt VI: It’s one of the many things that makes Atlassian such an enjoyable and rewarding place to work.