Home » Software Development » Atlassian » How to Add Licensing Support to Your Confluence Add-on

About Joel Patrick Llosa

Joel Patrick Llosa
I graduated from Silliman University in Dumaguete City with a degree in Bachelor of Science in Business Computer Application. After graduation, I became a Software Design Engineer at NEC Technologies Philippines, Inc. and NEC Telecom Software Philippines, Inc., mainly developing, maintaining and testing network management applications. I then left the software industry to start and manage my own coin-operated video gaming business. Currently, I'm a Java Developer freelancing in Upwork. I'm working on various Java projects at North Concepts Inc., Confluence Service and Predictive Technologies.

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.

Do you want to know how to develop your skillset to become a Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you our best selling eBooks for FREE!

 

1. JPA Mini Book

2. JVM Troubleshooting Guide

3. JUnit Tutorial for Unit Testing

4. Java Annotations Tutorial

5. Java Interview Questions

6. Spring Interview Questions

7. Android UI Design

 

and many more ....

 

 

Want to take your Java skills to the next level?

Grab our programming books for FREE!

Here are some of the eBooks you will get:

  • Spring Interview QnA
  • Multithreading & Concurrency QnA
  • JPA Minibook
  • JVM Troubleshooting Guide
  • Advanced Java
  • Java Interview QnA
  • Java Design Patterns