Atlassian Confluence Job Config Module Example
We are going to learn to create a simple Job Config Module for the Atlassian Confluence Server in this article. This Confluence Plugin Module Type is available in version 5.10 or later. The most common use of the Job Config Module is when we want our application to run scheduled and repeatable tasks. Let us begin.
1. Requirements
- Atlassian SDK
- Mars Eclipse
- Have created a simple Atlassian Confluence add-on. See Atlassian Confluence Add-on Development Examples if you haven’t done so.
- Familiar with Apache Maven.
2. Create the Plug-in
We create the plugin by running the atlas-create-confluence-plugin
command in our terminal. Type in com.javacodegeeks.example
for the groupId and job-config-module
for the artifactId. Accept the defaults for version and package by pressing ENTER. Press Y to finish and accept everything. This will create the stub code for our plugin.
3. Prune the Skeleton Code
Delete some of the unnecessary files that were generated. Our job-config-module directory should contain the following files:
pom.xml
README
LICENSE
- Under “src/main/resources” –
atlassian-plugin.xml
,job-config-module.properties
, css folder, images folder, js folder, and META-INF folder (all folders contain their default files) - Under “src/main/java” –
com.javacodegeeks.example
package and under itVerifyStatusPageJob.java
We have deleted the “test” directory which we will not need. We have also replaced the generated package and removed the generated java files in it. We will be creating the VerifyStatusPageJob.java
later.
4. Modify the pom.xml
We need to add a dependency and some import package entries in our pom.xml
. Check out Apache Maven if you don’t know where to insert the lines below.
pom.xml
snipped... <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.3.6.RELEASE<version> <scope>provided</scope> </dependency> snipped... <!-- Add package import here --> <Import-Package> com.atlassian.sal.api.transaction, com.atlassian.confluence.xhtml.api, com.atlassian.confluence.pages, org.springframework.osgi.*;resolution:="optional", org.eclipse.gemini.blueprint.*;resolution:="optional", * </Import-Package> snipped...
The above shows that we will be using the Spring Framework library. We need to import com.atlassian.sal.api.transaction
, com.atlassian.confluence.xhtml.api
, and com.atlassian.confluence.pages
because we will be injecting some of its objects.
We then execute atlas-mvn eclipse:eclipse
to create our .classpath and .project files so that we will be able to import this Maven project into Eclipse.
5. Add the Job Config Module in the Plugin Descriptor
atlassian-plugin.xml
<atlassian-plugin key="${atlassian.plugin.key}" name="${project.name}" plugins-version="2"> <plugin-info> <description>${project.description}</description> <version>${project.version}</version> <vendor name="${project.organization.name}" url="${project.organization.url}" /> <param name="plugin-icon">images/pluginIcon.png</param> <param name="plugin-logo">images/pluginLogo.png</param> </plugin-info> <!-- add our i18n resource --> <resource type="i18n" name="i18n" location="job-config-module"/> <!-- add our web resources --> <web-resource key="job-config-module-resources" name="job-config-module Web Resources"> <dependency>com.atlassian.auiplugin:ajs</dependency> <resource type="download" name="job-config-module.css" location="/css/job-config-module.css"/> <resource type="download" name="job-config-module.js" location="/js/job-config-module.js"/> <resource type="download" name="images/" location="/images"/> <context>job-config-module</context> </web-resource> <job-config name="Verify Status Page" key="verifyStatusPageJobConfig"> <job key="verifyStatusPageJob" perClusterJob="true"/> <schedule cron-expression="0 0 1 * * ?" jitterSecs="10"/> <!-- everyday at 1am --> <managed editable="true" keepingHistory="true" canRunAdhoc="true" canDisable="true"/> </job-config> </atlassian-plugin>
Take note of the highlighted lines. It describes our Job Config Module. The job-config name represents how this job config will be referred to in Confluence. The key represents the internal name for the job. The key points to the JobRunner
component. The key is also the class name with a lowercased first letter. A true
perClusterJob means that the job will only run once in a cluster rather than on every node. The cron-expression means that the job is ran everyday at 1:00 a.m. The jitterSecs provides the random jitter to prevent many processes from doing similar things all at once. When editable is true
the job’s schedule can be edited. When keepingHistory is true
the job’s history persists and survives server restarts. When canRunAdhoc is true
the job can be executed manually. When canDisable is true
the job can be enabled or disabled.
6. Modify the Properties File
We add the line in our job-config-module.properties
like below. This text will be displayed in the Scheduled Jobs Administration Screen.
job-config-module.properties
#put any key/value pairs here scheduledjob.desc.verifyStatusPageJobConfig=Verify Space Status
7. Create the JobRunner Component
VerifyStatusPageJob.java
package com.javacodegeeks.example; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import com.atlassian.confluence.content.render.xhtml.DefaultConversionContext; import com.atlassian.confluence.content.render.xhtml.XhtmlException; import com.atlassian.confluence.pages.Page; import com.atlassian.confluence.pages.PageManager; import com.atlassian.confluence.xhtml.api.MacroDefinition; import com.atlassian.confluence.xhtml.api.MacroDefinitionUpdater; import com.atlassian.confluence.xhtml.api.XhtmlContent; import com.atlassian.plugin.spring.scanner.annotation.imports.ComponentImport; import com.atlassian.renderer.RenderContext; import com.atlassian.sal.api.transaction.TransactionCallback; import com.atlassian.sal.api.transaction.TransactionTemplate; import com.atlassian.scheduler.JobRunner; import com.atlassian.scheduler.JobRunnerRequest; import com.atlassian.scheduler.JobRunnerResponse; @Component public class VerifyStatusPageJob implements JobRunner { private final PageManager pageManager; private final XhtmlContent xhtmlContent; private final TransactionTemplate transactionTemplate; @Autowired public VerifyStatusPageJob(@ComponentImport PageManager pageManager, @ComponentImport XhtmlContent xhtmlContent, @ComponentImport TransactionTemplate transactionTemplate) { this.pageManager = pageManager; this.xhtmlContent = xhtmlContent; this.transactionTemplate = transactionTemplate; } @Override public JobRunnerResponse runJob(JobRunnerRequest request) { if (request.isCancellationRequested()) { return JobRunnerResponse.aborted("Job cancelled."); } transactionTemplate.execute(new TransactionCallback() { @Override public Void doInTransaction() { Page page = pageManager.getPage("ds", "Space Status"); // space key, page title page.setBodyAsString(updatePage(page)); return null; } }); return JobRunnerResponse.success("Job finished successfully."); } private String updatePage(final Page page) { try { return xhtmlContent.updateMacroDefinitions(page.getBodyAsString(), new DefaultConversionContext(new RenderContext()), new MacroDefinitionUpdater() { @Override public MacroDefinition update(MacroDefinition macroDefinition) { if (macroDefinition.getName().equals("status")) { macroDefinition.setTypedParameter("colour", "Yellow"); macroDefinition.setTypedParameter("title", "Pending"); } return macroDefinition; } }); } catch (XhtmlException e) { e.printStackTrace(); } return page.getBodyAsString(); } }
This class implements the JobRunner
interface which is a requirement for all Job Config Modules. We are able to inject objects like PageManager
using @ComponentImport
because we have imported this package in our pom.xml. Remember, we edited the pom.xml in the fourth step? On to where the action is, the runJob method is executed by the Atlassian Scheduler. We need to wrap our job execution in a transaction because we are making use of our own component which is not declaratively wrapped in a transaction via Spring configuration.
Our job searches for the “Space Status” page (we will be creating this page) in the “Demonstration Space”. The space key for “Demonstration Space” is ds. After that, the page is searched for the Status macro. The Status macro color and title are then changed to yellow and pending, respectively.
8. Try the Plugin
Let’s run the plugin by executing atlas-run
in the terminal. Use admin as the password and username. We can access Confluence at http://localhost:1990/confluence. Firstly, we will create the Space Status page. The Space Status page should look like the one below. Don’t forget that “UNDER REVIEW” is a Status macro.
Go to the Scheduled Jobs Administration section (click on the cog at the upper right corner) and locate the Verify Space Status job. Execute the job by clicking on Run. The Administration section looks like the one below.
Go back to the Space Status page or reload it and you should see that “UNDER REVIEW” has changed to “PENDING”. It should look like the one below.
9. Summary of the Job Config Module Example
That was easy, wasn’t it? To summarize, we must implement the JobRunner
interface to create a Job Config Module component. The main work of our Job must execute in a transaction. Our Job can execute in a scheduled manner based on a cron expression. The key name must be the same as the class name of the component. For more information, check out Job Config Module and Job Module.
10. Download the Source Code
This is an example of building a Job Config Module for Atlassian Confluence Server.
You can download the source code of this example here: atlassian-confluence-job-config-module-example.zip.