Tutorial: Omit web-resources when your plugin is unlicensed

Level of experience INTERMEDIATE
Time estimate 0:30
Applicability

Licensing add-ons for the Marketplace using UPM versions after 2.0

For demonstration, this tutorial uses JIRA 7.0, but applies to:

  • JIRA 6.4+
  • Confluence 5.6+

Any product that ships with webresources 3.1.0+

Tutorial overview

This tutorial will show you how to exclude resources from the front end when your plugin is unlicensed. Omitting these files will improve the performance of the pages that your plugin affects, when the license is missing.

Required knowledge

Concepts covered

This tutorial will cover the UrlReadingCondition interface—what it does, and why it is useful.

About these Instructions

These instructions are based on Mac OS X, but you can use any supported operating system. Similarly, any supported IDE can be used. Just use the equivalent operations for your specific environment.

This tutorial was last tested with webresources 3.1.0 and JIRA 6.4.

Plugin source

We encourage you to work through this entire tutorial. However, if you want to skip ahead or check your work when you have finished, you can find the plugin source code on Atlassian Bitbucket. Bitbucket serves a public Git repository containing the tutorial's code. To clone the repository, enter the following command:

$ git clone git@bitbucket.org:atlassian_tutorial/tutorial-licensed-conditions.git

Alternatively, you can download the source as a ZIP archive by choosing Download repository here: https://bitbucket.org/atlassian_tutorial/tutorial-licensed-conditions/downloads

Step 1. Start up your host product

In this step, you'll start up a product instance to see your add-on in the Universal Plugin Manager (UPM).

  1. Ensure you're in your plugin directory. If you followed the steps above exactly, your directory should be tutorial-licensed-conditions

    $ cd tutorial-licensed-conditions/
  2. Issue the following command to start up JIRA: 

    $ atlas-run --product jira --version 6.4 

    The JIRA URL will be displayed in your terminal's output when finished, usually within a few minutes. 

    [INFO] jira started successfully in 42s at http://localhost:2990/jira
    [INFO] Type Ctrl-D to shutdown gracefully
    [INFO] Type Ctrl-C to exit
  3. Navigate to your JIRA instance, usually at:
    http://localhost:2990/jira
  4. Log in with the credentials admin/admin.
  5. Navigate to the   > Add-ons
  6. Click Manage add-ons in the left-nav.

    You can also access this page directly at http://localhost:2990/jira/plugins/servlet/upm.

  7. Verify that you can see your add-on listed in the UPM.

Step 2. Add a web-resource to your plugin

Here, we'll add a web-resource to the plugin that adds a flag to the view issue page, letting them know our plugin is licensed. Normally this kind of thing will irritate and annoy your end-users, so it's probably best to only use this code for demonstrating and testing web-resource conditions. 

  1. Open your plugin project.
  2. Open your atlassian-plugin.xml descriptor from the /src/main/resources directory.
  3. Somewhere before the closing </atlassian-plugin> tag, add a web-resource definition:

    <web-resource key="tutorial-view-issue-resources" name="tutorial-licensing Web Resources for the View Issue page">
        <dependency>com.atlassian.auiplugin:ajs</dependency>
        <resource type="download" name="issue.css" location="/css/issue.css"/>
        <resource type="download" name="issue.js" location="/js/issue.js"/>
        <context>jira.view.issue</context>
    </web-resource>
  4. Save and close the atlassian-plugin.xml descriptor file. 

    Your descriptor file should look like this:

    <atlassian-plugin key="${project.groupId}.${project.artifactId}" 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>
        <!-- add our i18n resource -->
        <resource type="i18n" name="i18n" location="tutorial-licensing"/>
        
        <!-- add our web resources -->
        <web-resource key="tutorial-licensing-resources" name="tutorial-licensing Web Resources">
            <dependency>com.atlassian.auiplugin:ajs</dependency>
            
            <resource type="download" name="tutorial-licensing.css" location="/css/tutorial-licensing.css"/>
            <resource type="download" name="tutorial-licensing.js" location="/js/tutorial-licensing.js"/>
            <resource type="download" name="images/" location="/images"/>
            <context>tutorial-licensing</context>
        </web-resource>
    
        <web-resource key="tutorial-view-issue-resources" name="tutorial-licensing Web Resources for the View Issue page">
            <dependency>com.atlassian.auiplugin:ajs</dependency>
    
            <resource type="download" name="issue.css" location="/css/issue.css"/>
            <resource type="download" name="issue.js" location="/js/issue.js"/>
            <context>jira.view.issue</context>
        </web-resource>
        <!-- publish our component -->
        <component key="myPluginComponent" class="com.example.plugins.tutorial.MyPluginComponentImpl" public="true">
            <interface>com.example.plugins.tutorial.MyPluginComponent</interface>
        </component>
    
        <!-- import from the product container -->
        <component-import key="applicationProperties" interface="com.atlassian.sal.api.ApplicationProperties" />
        <component-import key="pluginLicenseManager" interface="com.atlassian.upm.api.license.PluginLicenseManager"/>
    
    </atlassian-plugin>
  5. Create an issue.css file in the /src/main/resources/css directory.
  6. Create an issue.js file in the /src/main/resources/js directory. Open the file and add the following code to add a flag to the page: 

    // You can read about how AMD works here: https://github.com/amdjs/amdjs-api/wiki/AMD
    require([
        'jira/flag',
        'jquery'
    ], function(
        flag,
        $
    ) {
        var titleText = "MyPlugin is licensed!";
        var messageText = "I just thought you'd like to know that.";
        $(function() {
            flag.showInfoMsg(titleText, messageText);
        });
    });
  7. Save and close the issue.js file.
  8. The QuickReload plugin will automatically pick up your resources changes.
  9. Navigate to an issue page in JIRA.
  10. Verify that the contents of the issue.css and issue.js files are output on the page, via your browser's console. At this point, since we're using atlas-run, they will be served as individual files. 


    Your resource has now been added to the view issue page. However, note that the flag's message is inaccurate; the plugin is outputting the code irrespective of whether the plugin is licensed or not. Let's change that!

Step 3. Create a new ConditionEvaluator class

Currently, your web-resource is being added to the view issue page irrespective of whether your plugin is licensed or not.

Your plugin may already have licensing rules. How you choose to license your plugin is beyond the scope of this tutorial. Here, you will implement a ConditionEvaluator class which will be a proxy to your plugin's licensing logic.

ConditionEvaluator.java
package com.example.plugins.conditions;

public interface ConditionEvaluator
{
    public boolean evaluate(ConditionType type);
}
ConditionType.java
package com.example.plugins.conditions;
 
public enum ConditionType
{
    LICENSED;
}
ConditionEvaluatorImpl.java
package com.example.plugins.conditions;
 
import com.example.plugins.conditions.ConditionEvaluator;

public class ConditionEvaluatorImpl implements ConditionEvaluator
{
    @Override
    public boolean evaluate(ConditionType type)
    {
        switch (type)
        {
            case LICENSED:
                return isLicenseValid();
            default:
                return false;
        }
    }
    private boolean isLicenseValid()
    {
        // Implement your logic for determining if
        // your plugin has a valid, active license.
    }
}

Your plugin should be importing the PluginLicenseManager from UPM. If not, refer to the previous tutorial: Tutorial: Adding licensing support to your add-on.

You can implement your license check in the isLicenseValid() method. Refer to the Add-on licensing for developers documentation for more information on licensing.

Step 4. Add a component declaration for ConditionEvaluator to your plugin

The ConditionEvaluator class has been built, but currently it cannot be used in other classes in your plugin. That's because Spring and OSGi don't know about the component yet. In order to inject it in to your other classes and components, you'll need to add a <component> entry to your atlassian-plugin.xml file.

  1. Open your  atlassian-plugin.xml  descriptor from the  /src/main/resources  directory.
  2. Somewhere before the closing </atlassian-plugin> tag, add a web-resource definition:

    <web-resource key="tutorial-view-issue-resources" name="tutorial-licensing Web Resources for the View Issue page">
        <dependency>com.atlassian.auiplugin:ajs</dependency>
        <resource type="download" name="issue.css" location="/css/issue.css"/>
        <resource type="download" name="issue.js" location="/js/issue.js"/>
        <context>jira.view.issue</context>
    </web-resource>
  3. Save and close the atlassian-plugin.xml descriptor file. Your descriptor file should look like this:

    <atlassian-plugin key="${project.groupId}.${project.artifactId}" 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>
        <!-- add our i18n resource -->
        <resource type="i18n" name="i18n" location="tutorial-licensing"/>
        
        <!-- add our web resources -->
        <web-resource key="tutorial-licensing-resources" name="tutorial-licensing Web Resources">
            <dependency>com.atlassian.auiplugin:ajs</dependency>
            
            <resource type="download" name="tutorial-licensing.css" location="/css/tutorial-licensing.css"/>
            <resource type="download" name="tutorial-licensing.js" location="/js/tutorial-licensing.js"/>
            <resource type="download" name="images/" location="/images"/>
            <context>tutorial-licensing</context>
        </web-resource>
    
        <web-resource key="tutorial-view-issue-resources" name="tutorial-licensing Web Resources for the View Issue page">
            <dependency>com.atlassian.auiplugin:ajs</dependency>
            <resource type="download" name="issue.css" location="/css/issue.css"/>
            <resource type="download" name="issue.js" location="/js/issue.js"/>
            <context>jira.view.issue</context>
        </web-resource>
     
        <!-- publish our components -->
        <component key="myPluginComponent" class="com.example.plugins.tutorial.MyPluginComponentImpl" public="true">
            <interface>com.example.plugins.tutorial.MyPluginComponent</interface>
        </component>
        <component key="myPluginConditionEvaluator" class="com.example.plugins.conditions.ConditionEvaluatorImpl" public="false">
            <interface>com.example.plugins.conditions.ConditionEvaluator</interface>
        </component>
    
        <!-- import from the product container -->
        <component-import key="applicationProperties" interface="com.atlassian.sal.api.ApplicationProperties" />
        <component-import key="pluginLicenseManager" interface="com.atlassian.upm.api.license.PluginLicenseManager"/>
    
    </atlassian-plugin>

    This condition evaluator can now be used in your condition implementation.

Step 5. Create a new IsPluginLicensedCondition class

Currently, your web-resource is being added to the view issue page irrespective of whether your plugin is licensed or not. This adds unnecessary resources to the page that slow down the application. Here, you will implement a condition that checks your plugin's licensed status, which you can use to control when your plugin's web-resources are visible.

Create a new Java class called IsPluginLicensedCondition. This class should extend the webresource framework's SimpleUrlReadingCondition class.

IsPluginLicensedCondition.java
package com.example.plugins.conditions;

import com.atlassian.plugin.webresource.condition.SimpleUrlReadingCondition;

public class IsPluginLicensedCondition extends SimpleUrlReadingCondition
{
    private ConditionEvaluator conditionEvaluator;

    public IsPluginLicensedCondition(final ConditionEvaluator conditionEvaluator)
    {
        this.conditionEvaluator = conditionEvaluator;
    }

    @Override
    protected boolean isConditionTrue()
    {
        return conditionEvaluator.evaluate(ConditionType.LICENSED);
    }

    @Override
    protected String queryKey()
    {
        // This string will be appended to URLs as a GET parameter
        // whenever 'isConditionTrue' returns true.
        // You should make the string short, but unique to your plugin.
        return "myPluginName";
    }
}

The SimpleUrlReadingCondition class is provided by the atlassian-plugins-webresource framework, and implements the UrlReadingCondition interface. Conditions that implement this interface are allowed to participate in the batching process, since they are able to affect their parent web-resource's URLs.

Step 6. Add the license check condition to your web-resource

Now your plugin has a condition to toggle the output of your web-resources, it's time to make use of it.

  1. Open your plugin's atlassian-plugin.xml file.
  2. Add a <condition> to each of your web-resources that should only be active when the plugin is licensed:

    <condition class="com.example.plugins.conditions.IsPluginLicensedCondition" />
  3. Save and close the atlassian-plugin.xml file. Your descriptor file should look like this: 

    <atlassian-plugin key="${project.groupId}.${project.artifactId}" 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>
        <!-- add our i18n resource -->
        <resource type="i18n" name="i18n" location="tutorial-licensing"/>
        
        <!-- add our web resources -->
        <web-resource key="tutorial-licensing-resources" name="tutorial-licensing Web Resources">
            <dependency>com.atlassian.auiplugin:ajs</dependency>
            <resource type="download" name="tutorial-licensing.css" location="/css/tutorial-licensing.css"/>
            <resource type="download" name="tutorial-licensing.js" location="/js/tutorial-licensing.js"/>
            <resource type="download" name="images/" location="/images"/>
            <context>tutorial-licensing</context>
        </web-resource>
    
        <web-resource key="tutorial-view-issue-resources" name="tutorial-licensing Web Resources for the View Issue page">
            <dependency>com.atlassian.auiplugin:ajs</dependency>
            <resource type="download" name="issue.css" location="/css/issue.css"/>
            <resource type="download" name="issue.js" location="/js/issue.js"/>
            <context>jira.view.issue</context>
            <condition class="com.example.plugins.conditions.IsLicensedCondition" />
        </web-resource>
    
        <!-- publish our component -->
        <component key="myPluginComponent" class="com.example.plugins.tutorial.MyPluginComponentImpl" public="true">
            <interface>com.example.plugins.tutorial.MyPluginComponent</interface>
        </component>
        <component key="myPluginConditionEvaluator" class="com.example.plugins.conditions.ConditionEvaluatorImpl" public="false">
            <interface>com.example.plugins.conditions.ConditionEvaluator</interface>
        </component>
    
        <!-- import from the product container -->
        <component-import key="applicationProperties" interface="com.atlassian.sal.api.ApplicationProperties" />
        <component-import key="pluginLicenseManager" interface="com.atlassian.upm.api.license.PluginLicenseManager"/>
    </atlassian-plugin> 

    Excellent! It's time to test whether the condition works.

Step 7. Verify the result in various licensed states

Here you'll check whether everything is wired properly, by inputting a test license to check that the web-resource behaves the way it should.

Check behavior in unlicensed state

To check the behavior in an unlicensed state:

  1. Navigate to the UPM in JIRA and find your plugin.
  2. Edit the license key field, and remove the license.
  3. Click Update.
  4. Your plugin should now be "Unlicensed."
  5. Navigate to an issue page in JIRA.
  6. Verify that the contents of the issue.css and issue.js files are not output to the page, via your browser's console. The files should be empty.


    Great! Our web-resource contents aren't affecting the page when we are not licensed!

Add a license to the plugin

To add a license to the plugin:

  1. Navigate to the UPM in JIRA and find your plugin.
  2. Enter the following in the License key field:

    AAABEA0ODAoPeNp9UE1Pg0AUvO+v2MSbCc0uQZOS7KEIUWMtpNJqGi9bfKUb4S3ZD7T/XgrqwYPv9
    mbezGTeRXn0NK8cZRHlPGZRHEW0SEsaMh6SFGxlVOeURlGCdbRRFaAFetCGdo2vFdI36KHRHRhLV
    r7dg8kPGztsgjNyY0Cexal0IELOw4DNA85J1svGj4xwxgOZrOzsciYrp3qY0Eep0AFKrCD77JQ5j
    TapN6PyNb5mw5Dc1BKVndwWrpHWKonkCUwP5j4Vye28DF422yh42O3ugoTxZ7KcagzsBt9Rf+AP8
    k/O90V56mAl24HPttkyL7L1b+1Etnut19BqB4sa0FkRXpHCm+ooLfz9wRfgrX9WMCwCFAkWHvhJC
    dutS3LcZ46iYgICDPQqAhQL76vdT4AYTQXBwl/wbw/MtQrP4w==X02dt

    This is a 60-second license key.

  3. Click Update.
  4. If accepted, a lozenge and a banner should appear to notify you that your plugin license expires soon:

  5. Navigate to an issue page.
  6. Verify that the contents of the issue.css and issue.js files are output on the page, via your browser's console. At this point, since we're using atlas-run, they will be served as individual files. 



    Congratulations! Your plugin will now omit web-resources when your plugin is unlicensed!

Further testing notes

  • Test other license states using Timebomb licenses for testing.
  • If you run out of licenses when using atlas-run or atlas-debug, you can shut down your JIRA instance by clicking CTRL+D in your terminal; and then running atlas-mvn clean to remove your target staging directory. 
Was this page helpful?

Have a question about this article?

See questions about this article

Powered by Confluence and Scroll Viewport