How to Add Licensing Support to Your Confluence Add-on
In this example we will cover how to add licensing support to your Confluence add-on in this article. A developer needs to make some money in order to survive in this ever changing world. One of the ways we make a living is by licensing our software product. So let’s get to it and make some bread.
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.
- Be familiar with Apache Maven.
- A copy of unit-test-confluence-addon.tar.gz from How to Unit Test Your Confluence Add-on. We will add a license to this plug-in.
2. Add Licensing Dependencies in pom.xml
After extracting the compressed file (unit-test-confluence-addon.tar.gz), add the dependencies and the package import in your pom.xml
as highlighted below.
pom.xml
<dependency> <groupId>com.atlassian.upm</groupId> <artifactId>licensing-api</artifactId> <version>2.21.4</version> <scope>provided</scope> </dependency> <dependency> <groupId>com.atlassian.upm</groupId> <artifactId>upm-api</artifactId> <version>2.21</version> <scope>provided</scope> </dependency> ... snipped ... <Import-Package> com.atlassian.upm.api.license, org.springframework.osgi.*;resolution:="optional", org.eclipse.gemini.blueprint.*;resolution:="optional", * </Import-Package>
Run atlas-mvn eclipse:eclipse. This should generate the necessary files so that we can import the project in Eclipse.
3. Modify the Plugin Descriptor
We need to add a line in the atlassian-plugin.xml to enable licensing. Add the parameter atlassian-licensing-enabled
inside the plugin-info tag and set it to true.
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> <param name="atlassian-licensing-enabled">true</param> </plugin-info> ... snipped... </atlassian-plugin>
4. Add the Code for Licensing
SpacesMacro.java
package com.javacodegeeks.example; import java.util.List; import java.util.Map; import org.springframework.beans.factory.annotation.Autowired; 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.spaces.Space; import com.atlassian.confluence.spaces.SpaceManager; import com.atlassian.confluence.util.velocity.VelocityUtils; import com.atlassian.plugin.spring.scanner.annotation.component.Scanned; import com.atlassian.plugin.spring.scanner.annotation.imports.ComponentImport; import com.atlassian.upm.api.license.PluginLicenseManager; import com.atlassian.upm.api.license.entity.PluginLicense; @Scanned public class SpacesMacro implements Macro { private final SpaceManager spaceManager; private PluginLicenseManager licenseManager; @Autowired public SpacesMacro(@ComponentImport SpaceManager spaceManager, @ComponentImport PluginLicenseManager licenseManager) { this.spaceManager = spaceManager; this.licenseManager = licenseManager; } public SpacesMacro(@ComponentImport SpaceManager spaceManager) { this.spaceManager = spaceManager; } @Override public String execute(Map<String, String> parameters, String body, ConversionContext context) throws MacroExecutionException { String errorMessage = null; if (licenseManager.getLicense().isDefined()) { PluginLicense license = licenseManager.getLicense().get(); if (license.getError().isDefined()) { switch (license.getError().get()) { case EXPIRED: errorMessage = "Your license has expired."; break; case TYPE_MISMATCH: errorMessage = "License type mismatch."; break; case USER_MISMATCH: errorMessage = "License user mismatch."; break; case VERSION_MISMATCH: errorMessage = "License version mismatch."; break; case EDITION_MISMATCH: errorMessage = "License edition mismatch."; break; case ROLE_EXCEEDED: errorMessage = "License role exceeded."; break; case ROLE_UNDEFINED: errorMessage = "License role undefined."; break; default: // } } else { // handle valid license scenario } } else { errorMessage = "Unlicensed."; } if (errorMessage != null) { Map<String, Object> map = MacroUtils.defaultVelocityContext(); map.put("errorMessage", errorMessage); return VelocityUtils.getRenderedTemplate("templates/license-message.vm", map); } List spaces = spaceManager.getAllSpaces(); StringBuilder builder = new StringBuilder(); builder.append("<ul>"); for (Space space : spaces) { builder.append("<li>"); builder.append(space.getName()); builder.append("</li>"); } builder.append("</ul>"); return builder.toString(); } @Override public BodyType getBodyType() { return BodyType.NONE; } @Override public OutputType getOutputType() { return OutputType.BLOCK; } }
We created a new constructor and injected the PluginLicenseManager
. In our execute method, we added the licensing check at the beginning. We don’t want our macro to function if the user doesn’t have a license. First, we check if there is a license defined. If there is, we check if there is an error. Then we display the corresponding error message. Check out Atlassian licensing tutorial for more details.
Add the @Ignore
annotation in our unit test like so:
SpacesMacroTest.java
package com.javacodegeeks.example; import java.util.Arrays; import java.util.HashMap; import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.when; import static org.mockito.Mockito.verify; import com.atlassian.confluence.content.render.xhtml.ConversionContext; import com.atlassian.confluence.spaces.Space; import com.atlassian.confluence.spaces.SpaceManager; @RunWith (MockitoJUnitRunner.class) public class SpacesMacroTest { @Mock private SpaceManager spaceManager; @Mock private ConversionContext context; @Ignore @Test public void testBasicOperation() throws Exception { Space space1 = new Space(); space1.setName("Demonstration Space"); Space space2 = new Space(); space2.setName("Microsoft"); Space space3 = new Space(); space3.setName("Oracle"); when(spaceManager.getAllSpaces()).thenReturn(Arrays.asList(space1, space2, space3)); SpacesMacro macro = new SpacesMacro(spaceManager); String actual = macro.execute(new HashMap<String, String>(), "", context); String expected = "<ul><li>Demonstration Space</li><li>Microsoft</li><li>Oracle</li></ul>"; verify(spaceManager).getAllSpaces(); assertEquals(expected, actual); } }
It would be better to make a separate class for licensing so that we will be able to mock it and we could preserve our unit test instead of ignoring it. For now, we’ll focus on making the licensing work. I leave it to you to make a separate “License” class and integrate it to the macro.
5. Create the Template for the Error Message
It is recommended we follow the Atlassian design guidelines since we are developing in the Atlassian ecosystem. We will use the Atlassian User Interface front end library. We will utilize the Messages component of the AUI library. Create a templates folder under resources and create the file license-message.vm. Our template should look like the one below:
license-message.vm
#requireResource("confluence.web.resources:ajs") #requireResource("confluence.web.resources:jquery") <div class="aui-message aui-message-error"> <p class="title"> <strong>License Error!</strong> </p> <p>$errorMessage</p> </div>
6. Try the Macro without a License
Create a page and add our macro in it. If you still don’t know how to do that, I suggest you read my other articles.
Click on Create located at the top center of the dashboard. Choose a blank page then click create. Type in a title and then add our macro in the body of the page. Click Save and you should see something like this:
7. Try the Macro with a License
Click on the cog on the upper right corner of the page. Go to Manage add-ons. Look for the unit-testing macro. It should look like the one below:
Paste the 60-second license below on the License key text box and click update.
60-second License
AAABEA0ODAoPeNp9UE1Pg0AUvO+v2MSbCc0uQZOS7KEIUWMtpNJqGi9bfKUb4S3ZD7T/XgrqwYPv9 mbezGTeRXn0NK8cZRHlPGZRHEW0SEsaMh6SFGxlVOeURlGCdbRRFaAFetCGdo2vFdI36KHRHRhLV r7dg8kPGztsgjNyY0Cexal0IELOw4DNA85J1svGj4xwxgOZrOzsciYrp3qY0Eep0AFKrCD77JQ5j TapN6PyNb5mw5Dc1BKVndwWrpHWKonkCUwP5j4Vye28DF422yh42O3ugoTxZ7KcagzsBt9Rf+AP8 k/O90V56mAl24HPttkyL7L1b+1Etnut19BqB4sa0FkRXpHCm+ooLfz9wRfgrX9WMCwCFAkWHvhJC dutS3LcZ46iYgICDPQqAhQL76vdT4AYTQXBwl/wbw/MtQrP4w==X02dt
Go to Timebomb Licenses for Testing to test other kinds of licenses.
After adding the license, reload the page with the macro and it should look something like the one below:
8. How to Add Licensing Support to Your Confluence Add-on Summary
The first step to add licensing support to your Confluence add-on is to add the dependencies and import the needed packages in the pom.xml
. Next is to modify the plug-in descriptor to enable licensing. After that, we add the licensing code at the desired entry point. Lastly, we add a nice looking error message if a license error occurred.
9. Download The Source Code
This is a How to Add Licensing Support to Your Confluence Add-on example.
You can download the source code of this example here: licensing-support-confluence-addon.zip.