How to Develop a Plugin for Atlassian Jira

Atlassian’s Jira is a very popular tool for planning and tracking projects. The data that Jira accumulates is a gold-mine for obtaining insights about productivity improvements. 

4 minutes to read
With insights from...

Atlassian’s Jira is a very popular tool for planning and tracking projects. The data that Jira accumulates is a gold-mine for obtaining insights about productivity improvements. 

Introduction

The data that Jira accumulates is a gold-mine for obtaining insights about productivity improvements. While some of this data is readily accessible for analysis with out-of-the box Jira features, you are likely to want to gain specific insights about your projects that Atlassian developers did not foresee. This is where the Jira plug-in mechanism comes in handy as it enables you to crunch through Jira’s repository for your own purposes.

The following is a lengthy and quite technical blog post about obstacles overcome while developing a plugin to extract custom project metrics from Atlassian Jira 7.12.3. You may have come to this page seeking a solution to one of the following error messages:

Unexpected exception parsing XML document from URL 

[bundle://....0:0/META-INF/spring/atlassian-plugins-component-imports.xml]; nested exception is org.springframework.beans.FatalBeanException: Class [org.eclipse.gemini.blueprint.config.OsgiNamespaceHandler] for namespace [http://www.eclipse.org/gemini/blueprint/schema/blueprint] does not implement the [org.springframework.beans.factory.xml.NamespaceHandler] interface
Configuration problem: Unable to locate Spring NamespaceHandler for XML schema namespace
[http://www.eclipse.org/gemini/blueprint/schema/blueprint]
Offending resource: URL [bundle://....0:0/META-INF/spring/atlassian-plugins-component-imports.xml]
Cannot start plugin: Unresolved constraint in bundle
... [...]: Unable to resolve ....0: missing requirement [....0] osgi.wiring.package; (osgi.wiring.package=COM.jrockit.reflect)

Cannot start plugin: Unresolved constraint in bundle

... [...]: Unable to resolve ....0: missing requirement [....0] osgi.wiring.package; (osgi.wiring.package=com.atlassian.tutorial.myPlugin.api) [caused by: Unable to resolve ....0: missing requirement [....0] osgi.wiring.package; (osgi.wiring.package=org.springframework.osgi.service.exporter.support)]

'...-tests' - '...'  failed to load. The plugin has been disabled. A likely cause is that it timed out during initialisation. It has the following missing service dependencies : ...

lang.Exception: No tests found in class [it....]

No tests found

lang.NoClassDefFoundError: com/atlassian/jira/util/collect/MapBuilder

The article below records the steps I took to overcome the above issues. I don’t claim that it is the only way to solve them, just that it worked for me. Unfortunately, if you are new to Atlassian plugin development the learning curve can be very steep and the documentation can be hard to find. I hope I can save you a few hours or days of searching for the correct information. References to some relevant Atlassian documentation can be found at the end.

My sincere thanks to several Zuhlke colleagues who worked on the solution with me: Mirko Sciachero, Giovanni Asproni, Michael Dickens and Michael Scott.

agile metrics

A little architectural background

When I started this work, I knew nothing about the way the Jira server is built, but I soon discovered that I needed to be better informed if I was to have any chance of working out what was going wrong. The following diagram is a very simple-minded idea of the way in which dependencies are fulfilled within the web application.

The plugin we wish to develop runs within the context of a multi-threaded web app (Jira).

Jira Plugin Architecture simplified schematic

Integration/wired tests are run from a complementary plugin installed within the same environment.

Both plugins have dependencies on multiple Java libraries. A Java application satisfies its dependencies at runtime by calling its class loader to obtain them. Standard class loaders delegate to their parents first, and only start searching the locations on their class path if the parent is unable to load the required resource or class.

However, in the case of Jira it’s different. Jira wants to avoid every plugin loading copies of the same class, as this could easily increase the amount of memory used for class storage by a few orders of magnitude. It uses the OSGi framework, which comes with Spring, to provide a cache of already-loaded classes (called Felix) and to satisfy all dependencies from this cache.

This has important implications for the plugin configuration. Felix only loads classes permitted by the plugin configuration. This configuration is composed of instructions in the atlassian-plugin.xml file (a resource), the project POM, and some Atlassian defaults computed at build time.

The result is that it is very easy to encounter NoClassDefFound exceptions even though the required class is on the class path. Whenever this happens, it is worth checking the plugin configuration for missing package-import statements.

Contact person for United Kingdom

Immo Huneke

Expert Software Engineer

Immo Huneke has been with Zühlke since February 2004. Since completing a BSc in Physics and later qualifying as CEng MBCS CITP, he has gathered wide-ranging experiences in the fields of telecoms, finance, industrial manufacturing (including medical devices) and transport, among others. His passion is agile object-oriented software development and in the continuous improvement of the methods, techniques and tools used to achieve it.

Contact
Thank you for your message.