Creating an Atlassian Server Plugin

Shouvojit Sarker
agiledigital
Published in
8 min readJan 25, 2021

Thinking about extending Atlassian Jira or Confluence to do more for your team? This is the guide for you!

Car with plug resembling plugins
Photo by Ralph Hutter on Unsplash

The test of the machine is the satisfaction it gives you. There isn’t any other test. If the machine produces tranquillity it’s right. If it disturbs you it’s wrong until either the machine or your mind is changed. — Robert M. Pirsig, Zen and the Art of Motorcycle Maintenance

Jira and Confluence are Atlassian’s most widely used platforms to help teams of all sizes collaborate seamlessly. Jira is a platform that helps project teams collaborate around tasks, and Confluence is a collaborative space for teams to share knowledge. Both platforms are extensible meaning that you have the opportunity to create your own extensions to bring your team more tranquillity!

This is where plugins come in as a pathway to extend Atlassian platforms with your own creativity. Even as Atlassian transitions towards cloud-only offerings, plugin functionality continues to offer a great way to customise these systems.

Aside from the cloud offerings, Atlassian products have editions called Server (to be discontinued) and Data Center (enterprise on-premises solution starting from 500 users tier). Developers may create server plugins in-house to extend platform capabilities to suit custom requirements. In-house tech teams looking to write plugins are well supported in their first steps by existing plugin tutorials. However, there is more to do after getting past the HelloWorld stage!

This guide outlines motivations to create Atlassian plugins, introduces Atlassian’s plugin framework, and provides handy tips for writing, testing and installing in-house server and data centre plugins. We finish with a real-world example of a server plugin we’ve developed within our team.

What Can Plugins Do?

Extensible software gives you freedom. Rather than being limited to “out of the box” functionality, you can create features unique to your needs. This has led to most good software platforms being extensible. Extensibility allows platform vendors to focus on core functionality and allow customers to add additional functionality in a vendor-supported way.

Plugins are a common way of extending software platforms. For example, Tempo is a plugin that can help you track your time in Jira, export timesheets and create invoices all extended from the base Jira platform. In Confluence, many such add-ons are “blueprint plugins”. These extend Confluence to help organise new types of information, facilitate information searching, and encourage sharing consistency and collaboration between users.

Overview of Atlassian’s Plugin Framework

Plugin development framework
Overview of the Plugin structure

The Atlassian plugin framework is powered by OSGi. OSGi is a dynamic module system for Java designed to provide a vendor-independent, standards-based approach to modularising Java software applications and infrastructure. OSGi features used by the plugin framework include the service registry, lifecycle model, bundle dependency system and optional security layer. The service registry is where decoupling between implementation and consumers happen, allowing the extensibility of Atlassian products.

This means that each plugin is made up of one or more OSGi plugin modules. A plugin can have multiple features, but a single plugin module should provide a discrete part of a plugin.

Atlassian’s plugin framework can also be embedded in web applications that are not Atlassian products. This is freely available and can be used to write your own extensible applications. A team experienced in writing Atlassian plugins will be able to leverage that knowledge when using this framework.

Tips and Tricks

There are good tutorials for someone getting started with plugin development in the Atlassian ecosystem. Still, sometimes there will be problems to solve that are not directly covered in the tutorials.

The plugin SDK comes with convenient tools, and the application’s webapp can be overridden if required. Such instances could include disabling velocity caches during development, implementing custom themes and styling, making changes to webapp directory. You can override the application webapp by specifying the resources to add or override in the properties file.

Data management can take a bit of time to get used to, particularly the conventions and best practices around developing with Active Objects. Complex plugins may have unique requirements necessitating workarounds. For example, storing information that sometimes cannot be directly extracted from objects in a given context (for example, we had to store a redundant copy of the blueprint id while developing our Confluence plugin) and maintaining data integrity upgrades and transformations.

It can sometimes be difficult to confirm how the different components of a plugin are interacting with the core product. The Atlassian ecosystem supports standard remote debugging features of the JVM. Hence, as a last resort, it is possible to place breakpoints and pick apart the plugin framework's behaviour. The process of debugging plugins to understand their runtime behaviour may be unfamiliar to people without a background in Java or application server development.

Testing Your Plugin

Testing your plugin is similar to any Java-based web app, with a few additional details to keep in mind. For example, Mockito can be used to mock the Active Objects and test the DAO, but alternatively, a test Active Objects database can be seeded with test data and used for end-to-end tests. This requires the whole stack (DAO, Service, Servlet/REST Endpoint) to be built with the test database object.

For integration tests, a test instance can be used. This allows a developer to run and interact with an application instance in debug mode, allowing them to create integration test data interactively. However, this makes fixture data a black box, and modification is only possible through interaction with running instances. Traditional integration tests are also supported, with Selenium recommended for functional tests.

Performance and scale testing is a requirement to get data centre approval. Testing on a large instance is also a good idea for data centre apps.

Installing an In-House Plugin

Plugins can be directly installed by generating a jar file as part of CI/CD and uploading it to an application instance. If the plugin has been installed once already, a new version number must be specified in the pom file before packaging and generating another jar file. If the latest version breaks macro or data structure, it is necessary to dynamically migrate macro parameters and write an upgrade task.

Our Confluence Plugin — Idea Search

At Agile Digital, our team members have regular FedEx days to explore ideas and deliver some useful pieces of software. They often have a chance to build something between engagements to make our work smoother or more enjoyable. Ideas for those tasks often occur to people during the course of their work, while discussing related tech with their colleagues, or having a shower. We needed a way to capture them as they happened.

We tried a couple of approaches to collect those ideas, including a slackbot to look out for them and aggregate them in confluence, as well as directly entering unstructured data in Confluence. We didn’t want to capture them in JIRA, because not every idea is likely to be worked on and we didn’t quite want that much rigour.

Most recently, we’ve developed a Confluence plugin to capture, search and display ideas. The ideas are saved in a structure defined by a blueprint and stored in the database. The list of ideas can be viewed, updated and deleted, with typeahead implemented for searching and auto-completion. We plan on plugging our old slackbot into this plugin to combine both approaches.

How the Idea Search Plugin Works

The plugin relies on two important data flows: one from service to context for page rendering, another from service to REST endpoint to run queries using JavaScript.

Simplified component diagram of a confluence plugin
Component diagram

For both data flows, the Data Access Objects (DAO) are responsible for reading and updating data using Atlassian’s Active Objects framework. Our service uses these DAOs for processing requests from the API endpoint (e.g. to power typeahead searches) and the Blueprint context provider (for populating and extracting data from macros).

The blueprint context provider retrieves data from service to support page rendering and edits. Macros can be used to simplify the rendering of data that is repeated in multiple places. Macros are rendered by processing the data from the service through its macro context.

For rendering the blueprint index page, as can be seen from the diagram below, we first convert XHTML to HTML as some characters are not passable to the Confluence Storage Format renderer. Then we convert Confluence Storage Format data to HTML. We can then extract data, put in a context and send to a velocity template for the page to be rendered.

Blueprint index page rendering flow

Going the other way, extracting semi-structured data from the user-entered content requires a few processing steps. The representation of a macro is in Confluence Storage Format. Confluence Storage Formatted data is not well-formed XHTML, so to parse the content and extract the data, it is wrapped with a root element and doctype. This XHTML can then be parsed as XML, and elements can be easily extracted for persistence through the service.

Giving Our Plugin a Memory

As noted above, our confluence plugin uses Atlassian’s Active Objects as an ORM for data access and storage. This is implemented as a plugin in Atlassian applications, providing a data storage component that plugins can use to persist their private data.

Unlike Bandana, (another way of persisting data in Confluence) Active Objects uses a relational database instead of a key/value store. It is database-independent and isolates plugin data within a sandbox so that only the owner plugin can access it. Plugin data persisted in this way can be backed up by Confluence’s existing backup/restore mechanism.

This seamless integration led to us selecting Active Objects for data persistence.

Wrapping Up!

Developing plugins in-house is a beneficial exercise for adding custom capabilities to Atlassian server instances. These custom capabilities can meet your unique needs, increase efficiency and drive growth, just the way you want it! Unlike data centre or cloud, server licenses are perpetual. This means it can be used even after the support ends in 2024. For commercial plugin development, developing plugins for Atlassian cloud applications will be a great next step.

--

--

Shouvojit Sarker
agiledigital

A regular techie currently too involved with spatial data.