6 Tips for developing Atlassian Data Center Apps

(originally published in Medium)

In this article, I’ll share what my day-to-day work is like developing Java applications, primarily for Confluence.

Use Docker

All my projects contain a customized docker-compose.yml file, which allows me to start and stop different instances with just two clicks from within IntelliJ. This isolates every work, makes it extremely easy to set up a new container, and allows me to update the used Confluence (or Jira) version just changing the official Atlassian image number.

My basic configuration is bundled with a PostgreSQL service that has a database, user and password defined, and using the default port 5432 I can connect to the server through pgAdmin.

As for the Atlassian product, it already has all possible parameters configured (such as the license key, user, password, database, debugging settings…), so that just a few clicks after startup I have a working instance.

I also define some volumes, so that data is persisted and easily accessible from my system and, the most important point, I use Quickreload so that when I package my code it is automatically uploaded and reinstalled.

Docker also allows me to easily add any other services I might need, like the amazing MailHog email testing tool when I need to check the emails sent by my program.

The file look like this:

services:
postgresql:
image: postgres:14-alpine
container_name: example-project-postgres-container
environment:
POSTGRES_DB: confluence
POSTGRES_USER: confluence
POSTGRES_PASSWORD: xxxxxxxxx
ports:
- '5432:5432'
volumes:
- ./data/postgres:/var/lib/postgresql/data
networks:
- confluencenet

confluence-server:
depends_on:
- postgresql
image: atlassian/confluence:8.5.11
container_name: example-project-confluence-container
ports:
- '8090:8090'
- '5005:5005'
volumes:
- ./data/confluence:/var/atlassian/application-data/confluence
- ./releases:/home
environment:
JVM_MINIMUM_MEMORY: 2048m
JVM_MAXIMUM_MEMORY: 4096m
JVM_SUPPORT_RECOMMENDED_ARGS: "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005 -Dupm.plugin.upload.enabled=true"
ATL_TOMCAT_CONTEXTPATH: '/confluence'
ATL_JDBC_URL: jdbc:postgresql://example-project-postgres-container:5432/confluence
ATL_JDBC_USER: confluence
ATL_JDBC_PASSWORD: xxxxxxxxx
ATL_DB_TYPE: postgresql
ATL_DB_DRIVER: org.postgresql.Driver
ATL_DB_SCHEMA_NAME: public
ATL_LICENSE_KEY: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
networks:
- confluencenet

networks:
confluencenet: {}

Do the Atlassian Tutorials

Maybe this is an obvious point, but you should check the tutorials that you can find here. The main problem, as always with Atlassian, is that the documentation is often confusing and completely out of date. I’ve found several cases where, after cloning the repo, the code won’t compile because of updates that happened after the text was written. Even worse, for some reason the pages say they’ve been updated recently, when it’s clear they haven’t.

Atlassian Javadoc

This is my main reference when looking for how or what classes I need to use when developing some functionality. I wish the documentation was more informative and better maintained, but it’s still really useful.

Search in the Developer Community

Not as active as other software projects, there is really valuable information in this community, and you have different categories depending on the platform you are developing on. If you can’t find what you’re looking for, you can always start a new thread, but you won’t always get a response.

Separate your app logic from the Atlassian related services

This is not directly Atlassian-related advice, but a general principle of single responsibility. Anyway, I’ve seen too much Confluence-related stuff mixed with platform-independent logic, which makes any change or update much more difficult. And believe me, it is very common to have to update your code to keep up with changes Atlassian makes to libraries.

In my projects, you will find classes like ProjectNameSpaceService, which handles all space-related functionality like finding a space by name, creating a new one, or getting a list of spaces starting with XXXX. Following this structure, there will also be a ProjectNamePageService or a ProjectNameLabelService.

Test Locally

With our Docker setup, it’s really easy and fast to run tests on our machine before uploading the app to the customer’s test server. Here are a few points:

  • Unit testing: Mockito will help you here, and you will see if your design is correct or not, if your code is easy to test (or does not show some unnecessary dependencies).
  • Test data: when the needed data becomes more complex, or there has to be a large amount of it, you need to create it automatically. If it’s just some simple setting like a page or a space, you can do it manually.
  • Integration Tests: sometimes you want to test the functionality of your code, but you need some of the services provided by Atlassian.

As of July 2024, I’m still using Adaptavist’s Arquillian library for the last two points, but it hasn’t received any new commits in a while, and it can be considered outdated, so I’m researching new alternatives.

That’s all for now, enjoy!