Confluence 7.0 developer guide

As we are about to release Confluence 7.0, we thought it would be a good time to share what goes on behind the scenes as a complement to all the information already available to you at Preparing for Confluence 7.0

So here are a set of routine updates and best practices our teams follow in order to maintain and nurture our code-base.

Maven

We use Maven to build and package our products, and in order to ensure a consistent development environment across all teams, we are relying on mvnvm to manage maven versions.

# macOS
brew install mvnvm

# unix
mkdir -p ~/bin \
   && curl -s https://bitbucket.org/mjensen/mvnvm/raw/master/mvn > ~/bin/mvn \
   && chmod 0755 ~/bin/mvn
   
cd my-confluence-plugin
echo "mvn_version=3.5.4" > mvnvm.properties

XML is messy (and so are we), so to cope with that, we run the following command anytime we update our pom configurations

mvn tidy:pom

AMPS 8.0.x upgrade

We use the Atlassian Maven Plugin Suite ( AMPS ) to run and debug our plugins. As part of the 7.0 platform release we have upgraded all of our plugins to version 8.0.2

<properties>
    <amps.version>8.0.2</amps.version>
</properties>

One of the notable differences in this new stream of amps is the naming of the plugins it supplies :

amps 6.3.x amps 8.0.x
maven-amps-plugin amps-maven-plugin
maven-amps-dispatcher-plugin amps-dispatcher-maven-plugin
maven-bamboo-plugin bamboo-maven-plugin
maven-confluence-plugin confluence-maven-plugin
maven-crowd-plugin crowd-maven-plugin
maven-fecru-plugin fecru-maven-plugin
maven-jira-plugin jira-maven-plugin
maven-refapp-plugin refapp-maven-plugin
maven-stash-plugin This plugin was removed and replaced by bitbucket-maven-plugin

We also set the following properties to allow us to easily tune our build scripts without having to change the code too much

<properties>
    ...
    <jvm.args.custom/> <!-- Allows to specify custom arguments in build scripts -->
    <jvm.args.xmx>1024m</jvm.args.xmx>
    <jvm.args>-Xmx${jvm.args.xmx} ${jvm.args.custom}</jvm.args>
    
    <containerId>tomcat9x</containerId>
    <webdriver.browser>chrome</webdriver.browser>
    <atlassian.dev.mode>false</atlassian.dev.mode>
</properties>

We then consume these properties in our amps plugin configuration as follows:

<plugin>
    <groupId>com.atlassian.maven.plugins</groupId>
    <artifactId>confluence-maven-plugin</artifactId>
    <extensions>true</extensions>
    <configuration>
    ...
        <containerId>${containerId}</containerId>
         <jvmArgs>${jvm.args}</jvmArgs>
         <systemPropertyVariables>
             <webdriver.browser>${webdriver.browser}</webdriver.browser>
             <atlassian.dev.mode>${atlassian.dev.mode}</atlassian.dev.mode>
         </systemPropertyVariables>
    </configuration>
</plugin>

Platform 5 upgrade

Server Platform 5.0.0 is essentially the first platform release that adds support for running our Atlassian products on Java 11 while still maintaining compatibility with Java 8.

It consists of a set of upgrades to internal modules as well as third party libraries, most of which are introducing breaking changes. For a thorough list of all that we have upgraded see Preparing for Confluence 7.0 - Platform 5 upgrades

Dependency management

In order to align all of our plugins to consume the same dependencies, we use the following:

<properties>
    <confluence.version>7.0.1</confluence>
</properties>
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>com.atlassian.confluence</groupId>
            <artifactId>confluence-plugins-platform-pom</artifactId>
            <version>${confluence.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

The latter will bring into scope:

  • Spring framework 5.1.7.RELEASE
  • Guava 26.0-jre

With these in place you should expect a set of compile errors if your plugin is directly consuming the APIs that ended up with breaking changes, as well as run-time errors that are most likely related to the Spring upgrade.

Main gotchas in product code

Spring 5.x

It changed the default auto-wiring behaviour from “autodetect” to “constructor”, which means that setter wiring will stop working for

  • Components
  • REST Resources
  • Conditions

We have excluded Confluence Macros from this new behaviour.

If you encounter NullPointerException on components that rely on auto-wiring, then please double check you are using constructor injection instead of setters.

For example, we fixed the following error by updating our TaskListServlet and getting rid of all the setters in favour of final member fields and constructor injection.

Caused by: java.lang.NullPointerException
at com.atlassian.confluence.extra.dynamictasklist2.TaskListServlet.getEntity(TaskListServlet.java:770)
at com.atlassian.confluence.extra.dynamictasklist2.TaskListServlet$Action.<init>(TaskListServlet.java:573)
at com.atlassian.confluence.extra.dynamictasklist2.TaskListServlet$TaskListEditingAction.<init>(TaskListServlet.java:723)
at com.atlassian.confluence.extra.dynamictasklist2.TaskListServlet$AddTaskAction.<init>(TaskListServlet.java:370)

So, to recap, as part of our code writing/review best practices we always ensure our components are consistently following this pattern:

  • final member fields to remind us to use constructor injection
  • use the @Component, @Autowired annotations from Spring
  • use the @ComponentImport annotation from our Atlassian Spring Scanner
@Component
public class MyComponent {
    private final ContentService contentService;
    private final EventPublisher eventPublisher;
    
    @Autowired
    public MyComponent(@ComponentImport ContentService contentService,
                       @ComponentImport EventPublisher eventPublisher) {
        this.contentService = contentService;
        this.eventPublisher = eventPublisher;
    }
}
...
@Path("/")
public class MyResource {
    private final ContentService contentService;
    private final EventPublisher eventPublisher;
    
    @Autowired
    public MyComponent(@ComponentImport ContentService contentService,
                       @ComponentImport EventPublisher eventPublisher) {
        this.contentService = contentService;
        this.eventPublisher = eventPublisher;
    }
}

TaskManager API

The main difference is that com.atlassian.mail.queue.MailQueueItem no longer extends com.atlassian.core.task.Task which leads to compile errors around our task managers used in email notifications.

We essentially need to wrap the MailQueueItem in a lambda before passing it down to the task manager.

This API change is tied to this particular dependency upgrade

<dependency>
    <groupId>com.atlassian.mail</groupId>
    <artifactId>atlassian-mail</artifactId>
</dependency>

All compile errors can be fixed by updating usages of taskManager as follow:

taskManager.addTask(name, () -> item.send())

Deprecated code removal

You might have noticed that we’ve finally removed a good chunk of deprecated code. This is definitely not the most trivial thing to do on a product as mature as Confluence. See the full list of removed code paths.

So in order to nurture our code-base, we have been following these best practices whenever working on any of our plugins:

  • Inspect the project for deprecated code usage

  • Fix the confluence core deprecated usages by relying on the relevant replacement methods instead

We do have all sorts of builds and deprecation reports generated, that is how we measure our technical debt, however, nothing beats the good old IDE code inspection.

In general, we also enable the following on our static analysis builds:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <configuration>
       <showDeprecation>true</showDeprecation>
    </configuration>
</plugin>

Keep up to date with upcoming releases

We hope you found this information useful. As always you can find details of all changes at Preparing for Confluence 7.0.

We’re also making some small changes to how we communicate upcoming releases to you. We will post on the Atlassian Developer blog when a new beta is available, and also post an announcement here on Developer Community at the start of each release cycle with links to any resources. This will be instead of sending an email to the developers updates mailing list.

Confluence 7 was a long release for us, but we’ll be back to our regular cadence of a feature release approximately every 7 weeks very soon.

11 Likes