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.