Skip to end of metadata
Go to start of metadata

Applicable:

This tutorial applies to JIRA 4.4.x and JIRA 5.0.

Level of experience:

Advanced. Our tutorials are classified as 'beginner', 'intermediate' and 'advanced'. This one is at 'advanced' level. If you have never developed a plugin before, we advise you to try a beginner tutorial first.

 

On this page:

Source code

The source code of the plugin used in this tutorial is available in the Atlassian public source repository. You can check out the source code from Bitbucket:

New to Bitbucket? See Getting Started with Bitbucket.

Overview

In this tutorial, we're going to create a new Atlassian gadget for JIRA, bundle it inside a plugin, use a REST resource to provide it with data, and have the gadget talk to the resource. The gadget we'll make will list the projects in your JIRA instance that the current user can see.

Atlassian has implemented some of the OpenSocial specification in all of its applications. The gadget we'll write here is actually an OpenSocial gadget (also called a "Google Gadget") with some special sauce for JIRA. We'll assume a passing familiarity with gadgets, but in case they're new to you, there's a space devoted to gadget authoring.

Our gadget plugin will consist of the following components:

  • A gadget specification file to hold the gadget's XML and JavaScript.
  • Java classes implementing the REST resource the gadget will use.
  • Resources for display of the plugin UI.
  • A plugin descriptor to enable the plugin module in JIRA.

All these components will be contained within a single JAR file. Each component is further discussed in the examples below.

Step 1. Create the Plugin Project

Use the appropriate atlas-create-application-plugin command to create your plugin. For example, atlas-create-jira-plugin or atlas-create-confluence-plugin.

When prompted, enter the following information to identify your plugin:

  • group-id: com.atlassian.plugins.tutorial
  • artifact-id: jira-gadget-tutorial-plugin
  • version: 1.0-SNAPSHOT
  • package: com.atlassian.plugins.tutorial

Step 2. Create the Gadget Specification

For complete information on making a gadget spec, see the Atlassian Gadgets documentation.

First we will create the gadget spec file. This is src/main/resources/gadget.xml:

The <ModulePrefs> section is the metadata container for the gadget: title, directory title, description and so on. The <Content> section contains the HTML and/or JavaScript that drive the gadget's behaviour. We have left it empty here while we look more closely at <ModulePrefs>.

There are a few important things to notice:

  • The __MSG_gadget.title__ and __MSG_gadget.description__ properties are substituted at runtime by the gadget's message bundles, specified in <Locale> elements. The bundle is at src/main/resources/i18n/ALL_ALL.xml.
  • The <Locale> element tells the gadget where to find the message bundles. The messages attribute takes a URL. We'll show how to expose the message bundle in the next step.
  • The 'oauthpopup' and 'auth-refresh' items are needed for the gadget to authenticate to JIRA. See more about gadget authentication.
  • Finally, notice the <Optional> 'gadget-directory' feature. It specifies that the gadget is for JIRA and should be placed in the 'JIRA' category in the gadget directory browser. Without this, it is much harder to find and use gadgets from the directory browser.

Here's the XML message bundle itself under src/main/resources/i18n/ALL_ALL.xml:

Step 3. Customise the Plugin Descriptor and Maven POM

Now we need to edit the plugin descriptor at src/main/resources/atlassian-plugin.xml to give our plugin a unique key, and some meta information about this plugin.

For our plugin, we will start with a module declaration for the gadget spec:

A gadget is a module in atlassian-plugin.xml, like any other JIRA module. There are three required properties to note:

  • key must be unique for all modules in this plugin
  • name is the name under which the plugin module will be displayed in the JIRA administration console
  • location is the path to the gadget spec file, relative to src/main/resources

Next, we'll add the <resource> for the message bundle:

Building URLs to message bundles

Icon

At gadget render time, __ATLASSIAN_BASE_URL__ in the gadget's xml file will be automatically substituted with JIRA's configured base URL. The rest of the download URL is made from the atlassian-plugin.xml plugin key (in this case jira-gadget-tutorial-plugin) and the <resource>'s key value (in this case i18n/ALL_ALL.xml).

Below is the atlassian-plugin.xml for our plugin:

Finally, to support the included REST module, add the following dependencies to pom.xml in the <dependencies> element:

Now we are ready to write some code to make our gadget do something.

Step 4. Set Up a REST Resource

Our gadget will be consuming a REST resource to show how to get dynamic data into a gadget. A full discussion of how to write a REST resource is beyond the scope of this tutorial, but there is another tutorial for doing so: Writing REST services. We'll use an updated version of the resource developed in that tutorial in our plugin, as shown in our sample code. See the REST tutorial to learn how to set up your REST resource for this gadget (or just copy the REST stuff from this tutorial's sample code).

Step 5. Use JavaScript to Get the Projects into the Gadget

We will now develop JavaScript to call the REST resource and display the information in the gadget. At this point, you are strongly encouraged to read the documentation on using the Atlassian Gadgets JavaScript Framework, as it will help you understand the code we're about to write.

Returning to src/main/resources/gadget.xml, let's look at the <Content> section:

Here, take note of the following:

  • #requireResource()/#includeResources(), described in detail in the Atlassian Gadgets documentation, is used here to bring in the JIRA Gadget JavaScript framework. The directive #requireResource identifies a <web-resource> module inside a plugin, while #includeResources writes out the HTML tags for the resource in place. #includeResources is smart enough to write <style> tags for a CSS web resource, <script> for a JavaScript resource, and so on.

    Isn't that WebResourceManager?

    Icon

    If you've written Confluence or JIRA plugins before, you may recognise these directives as methods on the WebResourceManager.

  • var gadget = AJS.Gadget constructs a Gadget object. This object is from the JavaScript framework and is documented in the Atlassian Gadgets documentation. In this section, we specify the bare minimum to get a working gadget.
    • A gadget must know its base URL to operate properly. This is provided with the same __ATLASSIAN_BASE_URL__ system property that was used in the <Locale> element inside <ModulePrefs>.
    • The view object allows us to customise how the gadget is displayed on the page. It is documented in detail in the Atlassian Gadgets documentation.

We'll use the JavaScript framework's built-in support for Ajax to get the data we need from the resource. The code looks like this:

The view object's most important property is template, which is a function that generates the gadget's HTML. How you generate the HTML is up to you, but the most common approach is to use jQuery's manipulation API. Also, the gadget object itself is available as this inside template(), allowing use of the Gadget object API. template() takes a parameter args, which gives access to data made available in the view's args array. (We will use the jQuery manipulation API and the Gadget object in our gadget shortly.)

The args array is used to specify what data the gadget needs to use. Each member of the args array is a spec for remote data retrieval. A member object's key is the name we'll use to refer to the member after it's been passed to template(). ajaxOptions is an object whose properties are jQuery Ajax options. In this simple case, where we're making only a GET request to a REST service, the url property is all that's required.

Finally, we can write the code that will take the returned data and render it:

(info) Be careful of side effects! Whenever the gadget is reloaded or resized on the page, it will be re-rendered, and template() will be called. Make sure that doing so has no side effects.

Step 6. Build, Install and Run the Plugin

Follow these steps to build and install your plugin, so that you can test your code. If you have not already started the application, start it now:

  • Open a command window and go to the plugin root folder (where the pom.xml is located).
  • Run atlas-run (or atlas-debug if you might want to launch the debugger in your IDE).

Now we go to JIRA in the browser. Our gadget plugin has been installed into the application, and we can test our changes. If you don't already have a dashboard created, make one.

  • Click the Add Gadget menu option in the upper right.
  • Find the gadget called "Test JIRA Tutorial Gadget" in the list. This is our gadget. Add it to the dashboard.
  • You should see a list of projects viewable by all users display in the gadget.

(info) No projects appearing? Make sure you have at least one project in JIRA that is viewable by all users.

From this point onwards, you can use FastDev to reinstall your plugin behind the scenes as you work:

  1. Go to the FastDev page on your local JIRA site: http://localhost:2990/jira/plugins/servlet/fastdev.
  2. Do a hard refresh of the page:
    • Shift+Reload in most browsers.
    • Ctrl+Reload on Windows or in Internet Explorer.
    • In Safari 5, you will need to hold down the Shift key while clicking the Reload icon in the Location bar.
  3. Make any necessary changes to your plugin module.
  4. Go back to step 1.

As an alternative to FastDev, you can keep the application running in one command window and use the CLI (command line interface) in another window to dynamically re-install your plugin after each change.

  1. Open a new command window and go to the plugin's root folder (where the pom.xml is located).
  2. Run atlas-cli to start the CLI.
  3. Wait until you see a message, Waiting for commands.
  4. Run pi (plugin install) to compile, package and install the plugin.
  5. Go back to your browser. The updated plugin will have been installed into the application, and you can test your changes. (You may need to refresh the browser page first.)
  6. Make your changes in your IDE.
  7. Go back to step 1.

The full instructions are in the SDK guide.

RELATED TOPICS

Gadget Development

13 Comments

  1. Nice tutorial, but the referenced REST service example is out of date. Version 31 of the referenced tutorial is the last to include the actual code for getting project information; after that, it was entirely changed to a rather hello-world-style service. Please be so kind to either change the REST service tutorial back to its original intention, or update the Gadget tutorial in order to query the message service. The current state is tricky to understand because the examples and XML snippets neither match nor work together. Thanks (smile)

  2. Yes, please adapt the tutorial in order to use the REST resource presented in the linked REST tutorial !

    1. I agree. A fully-working gadget tutorial that consumes a REST resource would be nice to have. I am trying to write one at the moment, but am struggling without a end-to-end example.

      Thanks

  3. Well, the gadget is not working!!! As I understand, it is because of false REST tutorial. Could you, please, provide correct one. Or just give a source code. Thank you.

  4. Better way to do internationalization (i18n) is to use gadget.getMsg('string_id') method - especially if you want to keep you JavaScript in a separate file (and who would not want it, right?)

    If you ever wonder why you gadget has fixed width add <Require feature="dynamic-height"/> inside of ModulePrefs.

  5. Can't get any of these tutorials working.

    I'm simply importing the project created by atlas-create-jira-plugin

    and it always gives me an error message when importing it.  Searching for this error hasn't helped so far.

    10/28/10 11:12:16 AM PDT: Build errors for jira-gadget-tutorial.plugin; org.apache.maven.lifecycle.LifecycleExecutionException: Failed to execute goal com.atlassian.maven.plugins:maven-jira-plugin:3.2:compress-resources (default-compress-resources) on project jira-gadget-tutorial.plugin: Unable to execute mojo

    Here is some info that might help someone identify the issue:

    atlas-version returns this

    ATLAS Version:    3.2
    ATLAS Home:       G:\JIRA-sdk\atlassian-plugin-sdk-3.2
    ATLAS Scripts:    G:\JIRA-sdk\atlassian-plugin-sdk-3.2\bin
    ATLAS Maven Home: G:\JIRA-sdk\atlassian-plugin-sdk-3.2\apache-maven
    --------
    Executing: "G:\JIRA-sdk\atlassian-plugin-sdk-3.2\apache-maven\bin\mvn.bat" --version
    Apache Maven 2.1.0 (r755702; 2009-03-18 12:10:27-0700)
    Java version: 1.6.0_22
    Java home: C:\Program Files (x86)\Java\jdk1.6.0_22\jre
    Default locale: en_US, platform encoding: Cp1252
    OS name: "windows 7" version: "6.1" arch: "x86" Family: "windows"
    ATLAS Version:    3.2

    ATLAS Home:       G:\JIRA-sdk\atlassian-plugin-sdk-3.2

    ATLAS Scripts:    G:\JIRA-sdk\atlassian-plugin-sdk-3.2\bin

    ATLAS Maven Home: G:\JIRA-sdk\atlassian-plugin-sdk-3.2\apache-maven

    --------

    Executing: "G:\JIRA-sdk\atlassian-plugin-sdk-3.2\apache-maven\bin\mvn.bat" --version

    Apache Maven 2.1.0 (r755702; 2009-03-18 12:10:27-0700)

    Java version: 1.6.0_22

    Java home: C:\Program Files (x86)\Java\jdk1.6.0_22\jre

    Default locale: en_US, platform encoding: Cp1252

    OS name: "windows 7" version: "6.1" arch: "x86" Family: "windows"

    ..

    then I created the plugin skeleton using

    package: com.atlassian.plugins.tutorial

     Y: :

    [INFO] ----------------------------------------------------------------------------

    [INFO] Using following parameters for creating OldArchetype: jira-plugin-archetype:3.2

    [INFO] ----------------------------------------------------------------------------

    [INFO] Parameter: groupId, Value: com.atlassian.plugins.tutorial

    [INFO] Parameter: packageName, Value: com.atlassian.plugins.tutorial

    [INFO] Parameter: package, Value: com.atlassian.plugins.tutorial

    [INFO] Parameter: artifactId, Value: jira-gadget-tutorial-plugin

    [INFO] Parameter: basedir, Value: G:\JIRA-sdk\testapp\32test

    [INFO] Parameter: version, Value: 1.0-SNAPSHOT

    [INFO] ******************** End of debug info from resources from generated POM **********************

    [INFO] OldArchetype created in dir: G:\JIRA-sdk\testapp\32test\jira-gadget-tutorial-plugin

    [INFO] ------------------------------------------------------------------------

    [INFO] BUILD SUCCESSFUL

    [INFO] ------------------------------------------------------------------------

    [INFO] Total time: 33 seconds

    [INFO] Finished at: Thu Oct 28 11:56:58 PDT 2010

    [INFO] Final Memory: 27M/65M

    [INFO] ------------------------------------------------------------------------

    G:\JIRA-sdk\testapp\32test>

    The problem is, if I run eclipse and import this pom.xml, I just get that build error about how it can't execute mojo.

    1. Hi Omar, the previous edit to your post had a very useful bit of information, that you've since removed:

      The repository system is offline and the requested artifact is not locally available

      If you're using m2eclipse, then my best guesses are either:

      • You have m2eclipse configured to run in "offline mode" (that is, mvn -o);
      • m2eclipse is not using the Maven binaries and configuration bundled with the SDK.

      Or if you're using the maven-eclipse-plugin, then my best guess is that you haven't done a normal build from the command line (e.g. atlas-compile / atlas-run, etc.) to pull in the necessary project dependencies. Refer to the maven-eclipse-plugin docs for more information.

      1. Thanks for the suggestions.

        I finally got the skeleton to compile!!  

        What I had to do was go into the settings.xml and change the repository url's to http from https.  I guess the SSL versions of those repositories doesn't work from my desktop.  

        after that, it downloaded whatever it needed to and stopped giving me build errors.

        1. I'm guessing each step along the way I'm going to run into all sorts of errors with this maven plugin in eclipse.

          After adding the days-left-gadget.xml from the sample/tutorial the following new error shows up.  Checked the URL for the repo and it seems to have the folder its looking for....googled this error and found a couple people having the same problem but their solution didn't help.  

          10/29/10 10:48:02 AM PDT: [WARN] Failed to retrieve plugin descriptor for Plugin [org.apache.maven.plugins:maven-install-plugin]: Plugin org.apache.maven.plugins:maven-install-plugin:2.3 or one of its dependencies could not be resolved: The repository system is offline and the requested artifact is not locally available at C:\Users\omarh.HIRSCH\.m2\repository\org\apache\maven\plugins\maven-install-plugin\2.3\maven-install-plugin-2.3.jar
            org.apache.maven.plugins:maven-install-plugin:maven-plugin:2.3
          from the specified remote repositories:
            atlassian-public (http://m2proxy.atlassian.com/repository/public, releases=true, snapshots=true),
            central (http://repo1.maven.org/maven2, releases=true, snapshots=false)
          10/29/10 10:48:05 AM PDT: Build errors for jira-gadget-tutorial-plugin; org.apache.maven.lifecycle.LifecycleExecutionException: Failed to execute goal com.atlassian.maven.plugins:maven-jira-plugin:3.2:compress-resources (default-compress-resources) on project jira-gadget-tutorial-plugin: Unable to execute mojo
          10/29/10 10:48:10 AM PDT: Maven Builder: AUTO_BUILD 
          10/29/10 10:48:10 AM PDT: [WARN] Failed to retrieve plugin descriptor for Plugin [org.apache.maven.plugins:maven-deploy-plugin]: Plugin org.apache.maven.plugins:maven-deploy-plugin:2.4 or one of its dependencies could not be resolved: The repository system is offline and the requested artifact is not locally available at C:\Users\omarh.HIRSCH\.m2\repository\org\apache\maven\plugins\maven-deploy-plugin\2.4\maven-deploy-plugin-2.4.jar
            org.apache.maven.plugins:maven-deploy-plugin:maven-plugin:2.4
          from the specified remote repositories:
            atlassian-public (http://m2proxy.atlassian.com/repository/public, releases=true, snapshots=true),
            central (http://repo1.maven.org/maven2, releases=true, snapshots=false)
          10/29/10 10:48:02 AM PDT: [WARN] Failed to retrieve plugin descriptor for Plugin [org.apache.maven.plugins:maven-install-plugin]: Plugin org.apache.maven.plugins:maven-install-plugin:2.3 or one of its dependencies could not be resolved: The repository system is offline and the requested artifact is not locally available at C:\Users\omarh.HIRSCH\.m2\repository\org\apache\maven\plugins\maven-install-plugin\2.3\maven-install-plugin-2.3.jar

            org.apache.maven.plugins:maven-install-plugin:maven-plugin:2.3

          from the specified remote repositories:

            atlassian-public (http://m2proxy.atlassian.com/repository/public, releases=true, snapshots=true),

            central (http://repo1.maven.org/maven2, releases=true, snapshots=false)

          10/29/10 10:48:05 AM PDT: Build errors for jira-gadget-tutorial-plugin; org.apache.maven.lifecycle.LifecycleExecutionException: Failed to execute goal com.atlassian.maven.plugins:maven-jira-plugin:3.2:compress-resources (default-compress-resources) on project jira-gadget-tutorial-plugin: Unable to execute mojo

          10/29/10 10:48:10 AM PDT: Maven Builder: AUTO_BUILD 

          10/29/10 10:48:10 AM PDT: [WARN] Failed to retrieve plugin descriptor for Plugin [org.apache.maven.plugins:maven-deploy-plugin]: Plugin org.apache.maven.plugins:maven-deploy-plugin:2.4 or one of its dependencies could not be resolved: The repository system is offline and the requested artifact is not locally available at C:\Users\omarh.HIRSCH\.m2\repository\org\apache\maven\plugins\maven-deploy-plugin\2.4\maven-deploy-plugin-2.4.jar

            org.apache.maven.plugins:maven-deploy-plugin:maven-plugin:2.4

          from the specified remote repositories:

            atlassian-public (http://m2proxy.atlassian.com/repository/public, releases=true, snapshots=true),

            central (http://repo1.maven.org/maven2, releases=true, snapshots=false)

  6. Thanks Atlassian for this nice tutorial.

    So it's my understanding that each time I modify my gadget, I have to re-compile and package it in order to load the changes into JIRA.  Is there a simpler process to reload my changes?  What is the best way to debug my gadget?

    Thank you

  7. Anonymous

    What about tdd? Step 7 should be 1!

  8. Anonymous

    I'm newbie to writing plugins and gadgets.
    So I wanted to learn how to do this by using this tutorial.

    But at important parts there seems to be an error in displaying code-frames. All I can see, is (red highlighted):

    So it's pretty hard for me to follow without code-examples and whatever is hidden.

    Please fix this. Thank you.

  9. Anonymous

    Hi,

    I'm a newbie and trying to learn plugins and gadgets. I was wondering if there is any way to make gadgets from report plugin. To be precise, say, I have a working report plugin and how can I make a gadget out of it? Do I need to create a separate plugin for the particular report by following the tutorial?

    Thanks in advance!
    -Shantonu