Atlassian

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

  1. Atlassian SDK.
  2. Mars Eclipse.
  3. Have created a simple Atlassian Confluence add-on. See Atlassian Confluence Add-on Development Examples if you haven’t done so.
  4. Be familiar with Apache Maven.
  5. 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:

How to Add Licensing Support to Your Confluence Add-on
License Error Message

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:

How to Add Licensing Support to Your Confluence Add-on
Manage add-on

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:

How to Add Licensing Support to Your Confluence Add-on
Final Output

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.

Download
You can download the source code of this example here: licensing-support-confluence-addon.zip.

Joel Patrick Llosa

I graduated from Silliman University in Dumaguete City with a degree in Bachelor of Science in Business Computer Application. I have contributed to many Java related projects at Neural Technologies Ltd., University of Southampton (iSolutions), Predictive Technologies, LLC., Confluence Service, North Concepts, Inc., NEC Telecom Software Philippines, Inc., and NEC Technologies Philippines, Inc. You can also find me in Upwork freelancing as a Java Developer.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button