Virtually all organizations where it is necessary to maintain documentation and manage business processes are faced with the need to select and configure software that facilitates these processes. JIRA and Confluence from
Atlassian Software are quite popular in the Russian market. However, in their default configuration, they cannot solve all the tasks facing the organization - after all, each has its own vision of the work processes. In addition, requirements to customize JIRA grow as the product is used.
I work as a JIRA / Confluence developer at Mail.Ru Group, and I want to share the experience of writing plug-ins JIRA and Confluence: what can I do, how to do it and what I need not forget.
What I will tell you
There will be several posts on the development of various components. Plugins and source code can be found
here .
')
Plugin description will include:
- custom fields (custom fields), event listeners (listeners);
- servlets and REST, workflow programming.
In this article I will comment on working plugins, the source code of which can be found
here . To run the source is enough to read the
article .
Custom fields
So let's start with custom fields. Fields are the basic unit of data in JIRA. They contain issues data. Fields in JIRA as a whole can be divided into two main categories: system fields and user fields. System fields include various forms, such as text fields, drop-down lists, and others. In practice, basic functionality is often not enough, and we need to program our custom fields (for example, a field that calculates the sum of other fields or the number of comments).
The first plugin (Query Issues Custom Fields) is a list whose values are the requirement keys (issues) that are selected from the customized search query (JQL). The value of the list is the issue number.

Thus, the values are updated automatically each time you edit and view the field. Ready plugin is
here .
So, we want to develop a plugin that can be used for various projects, it is easy to use, and it works on all versions of JIRA.
To implement the first requirement, we need to organize a plugin data store where we can save the query (JQL) and other settings for the custom field being developed. To store a search query (JQL) it is advisable to use a data container for plug-ins (com.atlassian.sal.api.pluginsettings.PluginSettings). It should be noted that with this choice we will not have problems with the portability of the plug-in to other versions of JIRA. In addition, if you use the Java object serialization library (for example,
XStream ) to represent an object as a string, we can store data of any structure. To use the repository, you need to add the following lines to the
atlassian-plugin.xml plugin configuration file:
<component key="queryfields-config" name="Query issues custom fields plugin configuration" class="ru.mail.jira.plugins.lf.QueryFieldsMgrImpl"/> <component-import key="pluginSettingsFactory"> <interface>com.atlassian.sal.api.pluginsettings.PluginSettingsFactory</interface> </component-import>
and determine the interface that performs the required manipulations,
Interface source code public interface QueryFieldsMgr { boolean getAddNull(long cfId, long projId); String getQueryFieldData(long cfId, long projId); void setAddNull(long cfId, long projId, boolean data); void setQueryFieldData(long cfId, long projId, String data); }
and implement it:
Source code interface implementation import com.atlassian.sal.api.pluginsettings.PluginSettingsFactory; public class QueryFieldsMgrImpl implements QueryFieldsMgr { private static final String PLUGIN_KEY = "QUERY_LINKING_ISSUE_CFS"; private static final String VAL_SEPARATOR = "||"; private final PluginSettingsFactory pluginSettingsFactory; public QueryFieldsMgrImpl( PluginSettingsFactory pluginSettingsFactory) { this.pluginSettingsFactory = pluginSettingsFactory; } private String createPropKey(long cfId, long projId) { return (cfId + VAL_SEPARATOR + projId); } @Override public boolean getAddNull(long cfId, long projId) { String addNull = (String)pluginSettingsFactory.createSettingsForKey(PLUGIN_KEY).get(createPropKey(cfId, projId).concat(".addnull")); return Boolean.parseBoolean(addNull); } @Override public String getQueryFieldData( long cfId, long projId) { return (String)pluginSettingsFactory.createSettingsForKey(PLUGIN_KEY).get(createPropKey(cfId, projId)); } @Override public void setAddNull(long cfId, long projId, boolean data) { pluginSettingsFactory.createSettingsForKey(PLUGIN_KEY).put(createPropKey(cfId, projId).concat(".addnull"), Boolean.toString(data)); } @Override public void setQueryFieldData( long cfId, long projId, String data) { pluginSettingsFactory.createSettingsForKey(PLUGIN_KEY).put(createPropKey(cfId, projId), data); } }
After that, a specific data manager can be used in any servlets and extensions of standard Jira components, for example:
public class LinkerField extends TextCFType implements SortableCustomField<String> { private final QueryFieldsMgr qfMgr; public LinkerField( CustomFieldValuePersister customFieldValuePersister, GenericConfigManager genericConfigManager, QueryFieldsMgr qfMgr) { super(customFieldValuePersister, genericConfigManager); this.qfMgr = qfMgr; } ...
It is also a good practice to implement the administrative settings page of the plugin. On the page it makes sense to print a list of all project management fields and provide the page with controls to customize the field. The class that implements the administrative page is best made a heir from com.
atlassian.jira.web.action.JiraWebActionSupport . An example of a class is
email.jira.plugins.lf.QueryFieldsConfig . To connect to the plugin, it is enough to add the following lines to
atlassian-plugin.xml :
<web-item key="queryfields-configuration" name="Query issues custom fields configuration link" section="admin_plugins_menu/mailru-admin-section" weight="95"> <label key="queryfields.admin.title"/> <condition class="com.atlassian.jira.plugin.webfragment.conditions.JiraGlobalPermissionCondition"> <param name="permission">admin</param> </condition> <link linkId="queryfields-configuration">/secure/QueryConfigCfsConfig.jspa</link> </web-item> <webwork1 key="queryfields_conf" name="Query issues custom fields configuration" class="java.lang.Object"> <actions> <action name="ru.mail.jira.plugins.lf.QueryFieldsConfig" alias="QueryConfigCfsConfig"> <view name="input">/templates/queryfieldsconf.vm</view> <view name="success">/templates/queryfieldsconf.vm</view> </action> </actions> </webwork1>
For more details on how to implement JIRA URL actions, see
here .
In addition, it should be remembered that the administrative page should be protected from XSRF attacks. Read more about it
here .
If we cannot decide how we will display our field, then we can take a simple text field (
ru.mail.jira.plugins.lf.LinkerField ) as a
basis and expand it. It is impossible to build charts and collect statistics on text fields, but this functionality can be implemented by:
ru.mail.jira.plugins.lf.LirkerFieldSearcher ,
ru.mail.jira.plugins.lf.LirkerFieldCustomFieldValueProvider ,
ru.mail.jira.plugins.lf .LirkerFieldSearchInputTransformer and
en.mail.jira.plugins.lf.LirkerFieldCustomFieldRenderer .
After that, we just have to program the appearance for editing and the view:
edit-linkerfield.vm and
view-linkerfield.vm .
I want to note that if you do not know how to program a presentation, I advise you to see how the standard fields are implemented: webapp / WEB-INF / classes / templates / plugins / fields.
This approach can be used to implement custom fields of any complexity. For example, in this way you can implement a colorpicker, a list of calculated fields, formulas, and so on.
Event handlers
The second plugin I want to comment on is the JIRA MRIM Listener. This plugin sends event notifications to JIRA to Mail.Ru Agent. To create such a plugin, it is enough to implement only one event handler. Here is the class template:
Source public class MyListener implements InitializingBean, DisposableBean { private static Log log = LogFactory.getLog(MyListener.class); private EventPublisher eventPublisher; public MyListener ( EventPublisher eventPublisher) { this.eventPublisher = eventPublisher; } @Override public void afterPropertiesSet() throws Exception {
The source code is
here . It is worth noting that for filtering events here you can use the plugin's data warehouse, similar to that described in the previous example. Using an event handler, you can create related requests, sign file attachments, send HTTP requests, and more.
Instead of conclusion
For those who are interested in development, but have never done it yet, I want to recommend the book - the
JIRA Development Cookbook and keep track of free plugins at
https://market.atlassian.com/vendors/37127 .
That's all I wanted to tell you. If you have any questions or want to share your thoughts about JIRA and Confluence, I will be glad to talk with you in the comments. In addition, if you have ideas for a free plug-in, write, do, or help with the implementation.