As of Feb 15, 2024, Atlassian Marketplace will no longer offer sales and support for server apps. We are in the process of updating the server related information in DAC to reflect this change and some of the existing information may not align with the current experience. If you're considering cloud or have migrating customers, here's the step-by-step guide to help moving your app and customers to cloud.
Level of experience | INTERMEDIATE |
Time estimate | 0:30 |
Applicability | Licensing apps for the Marketplace For demonstration, this tutorial uses Jira Server 7.0, but applies to:
Any product that ships with webresources 3.1.0+ |
This tutorial will show you how to exclude resources from the front end when your app is unlicensed. Omitting these files will improve the performance of the pages that your app affects, when the license is missing.
This tutorial will cover the UrlReadingCondition
interface--what it does, and why it is useful.
This tutorial was last tested with webresources 3.1.0 and Jira 6.4.
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 app source code on Atlassian Bitbucket. Bitbucket serves a public Git repository containing the tutorial's code. To clone the repository, enter the following command:
1 2$ 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
In this step, you'll start up a product instance to see your app in the Universal Plugin Manager (UPM).
Ensure you're in your app directory. If you followed the steps above exactly, your directory should be tutorial-licensed-conditions
:
1 2cd tutorial-licensed-conditions/
Issue the following command to start up Jira:
1 2atlas-run --product jira --version 6.4
The Jira URL will be displayed in your terminal's output when finished, usually within a few minutes.
1 2[INFO] jira started successfully in 42s at http://localhost:2990/jira [INFO] Type Ctrl-D to shutdown gracefully [INFO] Type Ctrl-C to exit
Navigate to your Jira instance, usually at http://localhost:2990/jira
.
Log in with the credentials admin/admin
.
Navigate to the ** > Add-ons**.
Click Manage apps in the left-nav.
Here, we'll add a web-resource to the app that adds a flag to the view issue page, letting them know our app 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.
Open your app project.
Open your atlassian-plugin.xml
descriptor from the /src/main/resources
directory.
Somewhere before the closing </atlassian-plugin>
tag, add a web-resource definition:
1 2<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>
Save and close the atlassian-plugin.xml
descriptor file.
Your descriptor file should look like this:
1 2<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>
Create an issue.css file in the /src/main/resources/css
directory.
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:
1 2// 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); }); });
Save and close the issue.js
file.
The QuickReload app will automatically pick up your resources changes.
Navigate to an issue page in Jira.
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 app is outputting the code irrespective of whether the app is licensed or not. Let's change that!
Currently, your web-resource is being added to the view issue page irrespective of whether your app is licensed or not.
Your app may already have licensing rules. How you choose to license your app is beyond the scope of this tutorial. Here, you will implement a ConditionEvaluator class which will be a proxy to your app's licensing logic.
ConditionEvaluator.java
1 2package com.example.plugins.conditions; public interface ConditionEvaluator { public boolean evaluate(ConditionType type); }
ConditionType.java
1 2package com.example.plugins.conditions; public enum ConditionType { LICENSED; }
ConditionEvaluatorImpl.java
1 2package 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 app has a valid, active license. } }
Your app should be importing the PluginLicenseManager
from UPM. If not, refer to the previous tutorial: Adding licensing support to server apps.
You can implement your license check in the isLicenseValid()
method. Refer to the Server app licensing documentation for more information on licensing.
The ConditionEvaluator
class has been built, but currently it cannot be used in other classes in your app. 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.
Open your atlassian-plugin.xml
descriptor from the /src/main/resources
directory.
Somewhere before the closing </atlassian-plugin>
tag, add a web-resource definition:
1 2<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>
Save and close the atlassian-plugin.xml
descriptor file. Your descriptor file should look like this:
1 2<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.
Currently, your web-resource is being added to the view issue page irrespective of whether your app 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 app's licensed status, which you can use to control when your app's web-resources are visible.
Create a new Java class called IsPluginLicensedCondition
. This class should extend the webresource framework's SimpleUrlReadingCondition
class.
IsPluginLicensedCondition.java
1 2package 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 app. 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.
Now your app has a condition to toggle the output of your web-resources, it's time to make use of it.
Open your app's atlassian-plugin.xml
file.
Add a <condition>
to each of your web-resources that should only be active when the app is licensed:
1 2<condition class="com.example.plugins.conditions.IsPluginLicensedCondition" />
Save and close the atlassian-plugin.xml
file. Your descriptor file should look like this:
1 2<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.
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.
To check the behavior in an unlicensed state:
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!
To add a license to the app:
Navigate to the UPM in Jira and find your app.
Enter the following in the License key field:
1 2AAABEA0ODAoPeNp9UE1Pg0AUvO+v2MSbCc0uQZOS7KEIUWMtpNJqGi9bfKUb4S3ZD7T/XgrqwYPv9 mbezGTeRXn0NK8cZRHlPGZRHEW0SEsaMh6SFGxlVOeURlGCdbRRFaAFetCGdo2vFdI36KHRHRhLV r7dg8kPGztsgjNyY0Cexal0IELOw4DNA85J1svGj4xwxgOZrOzsciYrp3qY0Eep0AFKrCD77JQ5j TapN6PyNb5mw5Dc1BKVndwWrpHWKonkCUwP5j4Vye28DF422yh42O3ugoTxZ7KcagzsBt9Rf+AP8 k/O90V56mAl24HPttkyL7L1b+1Etnut19BqB4sa0FkRXpHCm+ooLfz9wRfgrX9WMCwCFAkWHvhJC dutS3LcZ46iYgICDPQqAhQL76vdT4AYTQXBwl/wbw/MtQrP4w==X02dt
This is a 60-second license key.
Click Update.
If accepted, a lozenge and a banner should appear to notify you that your app license expires soon:
Navigate to an issue page.
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 app will now omit web-resources when your app is unlicensed!
atlas-mvn clean
to remove your target
staging directory. Rate this page: