Introduction to Atlassian Confluence Plugins
If you’re familiar with Atlassian’s suite of products, you know how powerful Confluence can be for team collaboration and knowledge management. However, sometimes the out-of-the-box features just aren’t enough. This is where plugins come into play, allowing you to customize and extend Confluence to meet your specific needs. In this article, we’ll dive into the world of developing plugins for Atlassian Confluence using Java.
Setting Up Your Development Environment
Before you start coding, you need to set up your development environment. Here are the steps to get you started:
Installing the Atlassian Plugin SDK
The Atlassian Plugin SDK is your best friend when it comes to developing plugins. Here’s how you can install it:
# Download and install the SDK
wget https://www.atlassian.com/software/sdk/downloads/binary/atlassian-plugin-sdk-8.0.2.tar.gz
tar -xzf atlassian-plugin-sdk-8.0.2.tar.gz
cd atlassian-plugin-sdk-8.0.2
# Set up the SDK
./bin/atlas-setup
# Verify the setup
./bin/atlas-version
Using Maven for Plugin Development
Maven is a crucial tool for managing dependencies and building your plugin. Here’s an example of how to set up your pom.xml
file for a Confluence plugin:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example.plugins</groupId>
<artifactId>my-confluence-plugin</artifactId>
<version>1.0</version>
<packaging>atlassian-plugin</packaging>
<dependencies>
<dependency>
<groupId>com.atlassian.confluence</groupId>
<artifactId>confluence</artifactId>
<version>8.0.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>com.atlassian.maven.plugins</groupId>
<artifactId>confluence-maven-plugin</artifactId>
<version>8.0.2</version>
<extensions>true</extensions>
<configuration>
<productVersion>8.0.0</productVersion>
<productDataVersion>8.0.0</productDataVersion>
</configuration>
</plugin>
</plugins>
</build>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
</project>
Upgrading to Java 11
If you’re moving from an older version of Confluence to Confluence 8 or later, you’ll need to upgrade to Java 11. Here’s a tip from the community:
# Use a recent version of Maven
mvn --version
# Ensure your pom.xml is set up for Java 11
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
Creating Your First Plugin
Plugin Descriptor
The heart of any Confluence plugin is the atlassian-plugin.xml
file, which serves as the plugin descriptor. Here’s an example of what it might look like:
<atlassian-plugin key="com.example.plugins.my-confluence-plugin" name="My Confluence Plugin">
<plugin-info>
<description>This is my first Confluence plugin.</description>
<vendor-name>Maxim Zhirnov</vendor-name>
<vendor-url>https://example.com</vendor-url>
</plugin-info>
<!-- Define your plugin modules here -->
<module type="macro" key="my-macro">
<name>My Macro</name>
<class>com.example.plugins.MyMacro</class>
</module>
</atlassian-plugin>
Writing a Simple Macro Plugin
Macros are a great place to start because they provide immediate visual feedback. Here’s an example of a simple macro:
import com.atlassian.confluence.content.render.xhtml.ConversionContext;
import com.atlassian.confluence.macro.Macro;
import com.atlassian.confluence.macro.MacroExecutionException;
import com.atlassian.confluence.renderer.radeox.macros.MacroUtils;
import com.atlassian.confluence.util.velocity.VelocityUtils;
public class MyMacro implements Macro {
@Override
public String execute(Map<String, String> parameters, String body, ConversionContext context) throws MacroExecutionException {
// Simple macro that returns a greeting
return "Hello, World!";
}
@Override
public BodyType getBodyType() {
return BodyType.NONE;
}
@Override
public OutputType getOutputType() {
return OutputType.INLINE;
}
}
Adding Resources and Configuration
Adding Plugin Resources
Resources are non-Java files that your plugin might need. Here’s how you can add a resource:
<atlassian-plugin key="com.example.plugins.my-confluence-plugin" name="My Confluence Plugin">
<!-- ... -->
<resource type="download" name="my-resource" location="/path/to/my/resource.txt"/>
</atlassian-plugin>
Adding a Configuration UI
If your plugin requires configuration, you can add a configuration UI:
<atlassian-plugin key="com.example.plugins.my-confluence-plugin" name="My Confluence Plugin">
<!-- ... -->
<module type="web" key="my-config-ui">
<name>My Config UI</name>
<url>/plugins/servlet/my-config-ui</url>
<class>com.example.plugins.MyConfigUI</class>
</module>
</atlassian-plugin>
Advanced Plugin Development
Using Standard Page Decorators
If you’re developing a plugin that needs to work across multiple Atlassian products, you can use standard page decorators:
<atlassian-plugin key="com.example.plugins.my-confluence-plugin" name="My Confluence Plugin">
<!-- ... -->
<module type="web-panel" key="my-web-panel">
<name>My Web Panel</name>
<url>/plugins/servlet/my-web-panel</url>
<class>com.example.plugins.MyWebPanel</class>
<location>system.header</location>
</module>
</atlassian-plugin>
Accessing Confluence Components
To interact with Confluence components, you need to follow the Java package naming conventions to avoid conflicts:
import com.atlassian.confluence.core.ContentEntityObject;
import com.atlassian.confluence.core.ContentEntityManager;
import com.atlassian.confluence.user.ConfluenceUser;
import com.atlassian.confluence.user.UserAccessor;
public class MyPlugin {
public void doSomething(ContentEntityManager contentEntityManager, UserAccessor userAccessor) {
// Access Confluence components here
ConfluenceUser user = userAccessor.getCurrentUser();
// ...
}
}
Example Workflow: Creating a Jira Issue from Confluence
Here’s an example of how you might create a plugin to generate Jira issues from Confluence, a common use case:
Step-by-Step Process
Set Up the Plugin Skeleton: Use the Maven template to create a basic plugin skeleton.
Define the Plugin Descriptor: Add the necessary modules to your
atlassian-plugin.xml
.Create the Macro or Web Item: Write the Java code for your macro or web item that will trigger the Jira issue creation.
Use REST API or Webhooks: Use the Jira REST API or webhooks to create the issue.
Example Code
Here’s a simplified example using the Jira REST API:
import com.atlassian.confluence.content.render.xhtml.ConversionContext;
import com.atlassian.confluence.macro.Macro;
import com.atlassian.confluence.macro.MacroExecutionException;
import com.atlassian.httpclient.api.HttpClient;
import com.atlassian.httpclient.api.Request;
import com.atlassian.httpclient.api.Response;
import com.atlassian.httpclient.api.ResponseHandler;
import com.google.gson.Gson;
public class CreateJiraIssueMacro implements Macro {
private final HttpClient httpClient;
public CreateJiraIssueMacro(HttpClient httpClient) {
this.httpClient = httpClient;
}
@Override
public String execute(Map<String, String> parameters, String body, ConversionContext context) throws MacroExecutionException {
String jiraUrl = parameters.get("jiraUrl");
String projectKey = parameters.get("projectKey");
String issueType = parameters.get("issueType");
// Prepare the request
Request request = Request.Builder(Request.Method.POST, jiraUrl + "/rest/api/2/issue")
.setEntity(new Gson().toJson(new Issue(projectKey, issueType)))
.build();
// Execute the request
Response response = httpClient.execute(request, new ResponseHandler<Response>() {
@Override
public Response handle(Response response) throws Exception {
return response;
}
});
if (response.getStatusCode() == 201) {
return "Issue created successfully!";
} else {
return "Failed to create issue.";
}
}
// Helper class for the issue JSON
private static class Issue {
private String project;
private String issuetype;
public Issue(String projectKey, String issueType) {
this.project = new Project(projectKey);
this.issuetype = new IssueType(issueType);
}
private static class Project {
private String key;
public Project(String key) {
this.key = key;
}
}
private static class IssueType {
private String name;
public IssueType(String name) {
this.name = name;
}
}
}
}
Sequence Diagram
Here is a sequence diagram showing the interaction between Confluence, the plugin, and Jira:
Conclusion
Developing plugins for Atlassian Confluence is a powerful way to extend its functionality and tailor it to your team’s needs. From setting up your development environment to creating complex plugins that interact with other Atlassian products, the process involves several key steps. By following this guide, you’ll be well on your way to becoming a Confluence plugin master, ready to unleash the full potential of your team’s collaboration tools.
Remember, the world of plugin development is vast and full of possibilities. Don’t be afraid to experiment and push the boundaries of what you can achieve. Happy coding