Skip to end of metadata
Go to start of metadata


This tutorial applies to RefApp 2.12.x

Level of experience:

This is an intermediate tutorial. You should have completed at least one beginner tutorial before working through this tutorial. See the list of tutorials in DAC.

Time estimate:

It should take you approximately half an hour to complete this tutorial.


On this page:

About this tutorial

In this tutorial, you will create a plugin that exposes a REST service. Behind the scenes, Atlassian REST services are implemented with Jersey, which is the reference implementation of the JSR-311 standard.


Any REST API added by a plugin should resemble, as much as possible, the form of the REST interfaces already exposed by the platform and other plugins. This makes life easier for those who want to use your API. You can make sure your API fits in with the others by following the Atlassian REST API design guidelines.

We will create the service in a plugin built for the Atlassian reference application, RefApp. The RefApp encapsulates common components of the Atlassian platform. Thus, it serves as a platform against which you can create plugins targeted for multiple Atlassian applications.

Plugin source code

We encourage you to work through this tutorial. If you want to skip ahead or check your work when you are done, you can find the plugin source code on Atlassian Bitbucket. To clone the repository, issue the following command:

git clone

Alternatively, you can download the source using the get source option here:

Step 1. Create the plugin project

In this step, you use an atlas- command to create your plugin project. The atlas- commands are part of the Atlassian Plugin SDK, and automate much of the work of plugin development for you.

  1. Open a terminal and navigate to the directory where you want to create your REST plugin project home directory.
  2. Enter the following command:

  3. When prompted, enter the following values for your plugin project parameters:









  4. Confirm your entries when prompted.

The command creates a directory named Message with all the initial project files. This tutorial refers to this directory as your project home. Unless otherwise indicated, all directory paths are relative to the project home.

Step 2. Add the REST module

So far, we have a basic plugin project with a POM file, descriptor file, and an example servlet. Let's add the REST module to the project, again using the Atlassian Plugin SDK:

  1. Change to the new Message directory.
  2. Run the following command:

  3. Enter 7, REST Module Plugin. 
  4. At the prompts, enter values for the classname, package name, REST path on which to serve your resource, and version. Alternatively, you can accept the suggested values for these properties by just hitting enter. Here are the defaults:  
    • Classname: MyRestResource
    • Package Name:
    • REST path: /myrestresource
    • Version: 1.0 
    The remaining instructions assume the use of the default options. If you enter other values, you will need to adjust certain details, such as the URL for accessing the service.
  5. Enter N when prompted to show advanced setup options.
  6. Enter N when asked whether you'd like to add another plugin module.

The SDK finishes up, adding REST source files to the project. 

Step 3. Look over the source files

Navigate to the following directory: 


This directory contains two source files that implement the REST services


Open the files to have a look. Notice the contents of The getMessage() method returns the canonical "Hello World" message.

Also notice the annotations. These are JAXB annotations. For your reference, here's a rundown of what they are, starting with those in







Class or Method

Identifies the URI path that a resource class or class method will serve requests for. @Path annotations on methods are relative to the @Path annotation on the enclosing class.




Identifies that the class method will handle requests for a GET http message. Having more than one @GET annotation on a method in a class will fail unless @Path annotations are used to distinguish them. Other annotations are available such as DELETE, POST, HEAD, etc.  See:




Identifies that the method can be called without supplying user credentials. If this annotation did not exist then a session would need to be established with your application or you would need to pass in os_username and os_password parameters.




It specifies the content types the method may return. If this annotation is not present, the method may return any content type; if it is present, the method must return one of the types specified.



Class or enum

Maps a class or an enum type to an XML element.



Class or enum

Controls whether fields or properties are serialised by default



Property or field

Maps a property or field to an XML Attribute

We'll build on the source code—including the annotations—in a bit. But first let's try running the plugin.

Step 4. Run the plugin

Start the RefApp using the following SDK command. Thanks to some SDK magic, your plugin is loaded in the application automatically:

The first time you run the command, the application takes a few minutes to download packages and other resources needed for the RefApp.

When the application finishes starting up, you can use a REST browser or just a plain web browser to test the service. If you use the default project settings, the service is exposed at the following URL: 


Substitute <host> with the host name appropriate for your Atlassian application.

You should see a page similar to the following:

After confirming the service works, shut down the RefApp by returning to the terminal where you started the RefApp and entering Ctrl-C.

Step 5. Extend the plugin with parameters

We've got our "Hello World" message working, but let's make our plugin more flexible by parameterizing it. Specifically, we'll allow the caller to pass a parameter that is used as the resource identity (from the REST client's point-of-view). It also populates the body of the response message, replacing the "Hello World" string.

While this isn't necessarily a realistic example of a REST implementation, it illustrates at a high level two approaches for implementing REST services.

  1. Open for editing.
  2. Replace the getMessage() method with this:

  3. Add two new methods, one of which takes a path parameter and another that takes a query parameter.

  4. Open and add a new element declaration:

  5. Replace the class constructor with this one:

  6. Finally, add two new methods to the class:

Notice we've added a few more annotations: 







Property or field

Maps a property or field to an XML Element



Method Parameter

It maps a method variable to an element in the @Path.



Method Parameter

It maps a method variable to a query parameter.

That's it! We've parameterized the REST resource in a couple of ways: we've enabled the service to accept parameters as either a query parameter or as value in the path. In a more realistic implementation, the REST resources might be dynamically composed in some other manner, such as by using values from a database. The following tip provides more information on the two approaches used by our plugin.


When should I use PathParams or QueryParams?
Since you can map information from the request into the method in different ways, you may be wondering when to use PathParams over QueryParams. Here are some guidelines for you to think about.

Query parameters usually mean the resource will not be cached by a proxy or your browser. So if you are passing in an id to find some information about some sort of entity, then user a path parameter.

On the other hand, if your input is something that might be change with your data (like a user's full name), then you should use a query parameter. Your aim should be to provide a single authoritative URL for your resources so your clients can predict and cache its results. If a user got married and you used their name in the URL then it might change and caches and links would now fail.


Step 6. Adjust the test code

When we used the SDK to add the REST module to our project, the SDK helpfully added some tests for the code it generated. However, if we run atlas-run the same way we did before, we'll get a test build failure due to modifications we made to the source code. The method under test now expects a parameter.

We'll leave the full explanation of testing matters to another tutorial for now, and just make a small adjustment to our tests to get them working:

  1. Navigate to the test directory for the plugin:
  2. Open for editing.
    The test checks whether the getMessage() method returns the "Hello World" message. But the method, as we've modified it, now expects a parameter. We'll hard-code one.
  3. Add an input value to the getMessage() method invocation. Since our test still expects our original "Hello World" message in the response, add that. As modified, the relevant line of code should look like this: 


Step 7. Try it again, this time testing with the REST API Browser

Let's try the plugin again but this time let's use the REST API Browser (RAB), a page for browsing and testing REST APIs built into the RefApp (and every other Atlassian application). Developers who want to use your REST service would use the browser to discover and test the REST interface.

  1. Start the RefApp again: 

  2. In a web browser, open the RefApp application:
  3. Log in with the default username and password, admin/admin.
  4. Click on the username link at the top right, A.D.Ministrator.
  5. Click on REST API Browser. This link appears in the Developer Toolbox section of the left side menu.
    The browser shows one of the services exposed by the application, along with the resources in that service. 
  6. Click on the link at the top of the page that identifies the active service.
    This is likely Applinks Product Plugin, but could be something else if you've installed other modules or have another version of the RefApp. 

  7. Choose Message from the list of services. This is the plugin service we created.
    The REST browser shows two resources for our service: /message and /message/{key}. The second is the path parameter resource, and the first takes a parameter as a query parameter.
  8. Enter a key value for either of the resources and click Execute. The key value can be any string you like, such as "greeting".
    The results appear below the resource.

  9. Experiment by trying the other resource as well. Notice that if you click execute without a parameter, you'll get our default response, "Hello World."


    For information on adding documentation for your resources and parameters (which REST browsers such as this one can display), see Documenting your APIs with the Atlassian REST API Browser.

As a further exercise, try installing the plugin to another Atlassian application, such as JIRA. Start up JIRA in another terminal using the atlas-run-standalone command, and use the UPM to install the JAR file directly from the <project_home>/target directory. Just as you did with the RefApp, you can open the REST API Browser from the Administration Console of the application and test the Message service.

Next steps

Congratulations! You've created and parameterized a REST resource exposed by your Atlassian plugin. To continue learning about REST in Atlassian plugins, see the application-specific documentation on building REST services, including:

For general information on using REST for Atlassian plugin development, see:




  1. A request: add "Step 0: include REST Plugin Module in your Confluence deployment"

  2. Maybe I am missing something, but I tried to blindly follow this tutorial on 2 different clean Atlassian Plugin SDK (3.0.2) setups (one Vista, one Ubuntu) and it does not compile, because of missing servlet-api dependency.

    package javax.servlet.http does not exist

    When I added to pom.xml such dependency:

    then it worked fine. BTW such dependency is included in the first JIRA tutorial (writing gadgets).

    Another annoying thing: when I copy and paste code snippets to IDE (or wherever), I end up with every pasted line preceded with line number and I have to manually (or rather with regexps (wink)) clean it up, e.g.:

    <atlassian-plugin key="rest-service-tutorial-plugin"
    02.                  name="REST Service Tutorial Plugin" plugins-version="2">
    03.    <plugin-info>
    04.        <description>A sample plugin showing how to add a REST service to JIRA.</description>
    05.        <version>1.0</version>
    06.        <vendor name="Atlassian" url="" />
    07.        <application-version min="4.0"/>
    08.    </plugin-info>
    10.    <!--
    11.        Automatically finds all JAX-RS resource classes in the plugin and

    Any chance to improve/reconfigure code macro here?

    1. +1 on the line number thing. It's really annoying! In the end I just downloaded the whole source and cut and paste from there.

    2. The line number business is odd. Didn't happen for me, until I realized I was using Chrome. Then I tried in Firefox and boom, there it is.

      Opened NCODE-67.

  3. +1 on the line numbers.

    +1 on the same error as Wojcech.

    However when I add the servlet API to my pom, when I do "pi" I get from jira:

    cheers, jamie

    1. But it works fine on a different box...

      Update: This turned out to be because the download of the jira war file was corrupt... --strict-checksums from now on.

      1. As in mvn -C. Seems like a reasonable default to have added to all of the sdk commands. I wonder what the overhead is.

  4. Never mind -- found the parent pom! Its at

    At the top of the page is a link to the SVN repository for the tutorial sources.  I checked out the Jira REST tutorial with the command:

    And then switched to the jira-rest-service-plugin and tried to build. The build failed because it could not locate the parent pom

    Reason: Cannot find parent: com.atlassian.plugins.tutorial:rest-service-tutorial-plugin-parent for project: com.atlassian.plugins.tutorial:jira-rest-service-plugin:atlassian-plugin:null for project com.atlassian.plugins.tutorial:jira-rest-service-plugin:atlassian-plugin:null

    And I can't find the parent pom in SVN repository. Any idea where one can find:

  5. I'm really interested in seeing the "Accessing Confluence Resources" section. I have a nice REST service I am trying to access from a confluence instance but the REST service keeps giving up "Failed to lazily initialize a collection - no session or session was closed" whenever I add

    to my service. Basically, I'm writing a macro plugin that calls the REST service. Does it lose context?

      1. Sorry, not understanding. I'm not seeing any of the classnotfound and dependency errors noted in the linked article.

        1. When you get a lazy initialization exception at runtime, it means that the app couldn't instantiate the class it was looking for. The linked article describes some of the ways this can happen. In your case, the contentEntityManager is probably not getting created as desired.

          1. The thing is: I'm pretty sure it's getting the class. If I have these three lines

            then I get the session closed error, but if I comment out the last one like this:

            I don't get the error, and in fact I can use the title or the id, which are being returned with the correct values.

            1. It's probably the getContent call that makes the lazy contentEntityObject actually go off and do something and in the process of doing that something, things go wrong.

      2. Hi Matt,

        I didn't understand this problem too...

        Could you appoint me what I did wrong ?? Im using Confluence 3.1.1 and SDK 3.1.3.

        I've just create my first plugin and I got the same problem here.

        I just create the following snippet inside Resource method:

        In reality, every method on Page that needs some join fails this way.

        Thanks for any help.

  6. Hi Guys,

    I'm completing 10 long hours trying to access data from Page objects inside my Rest service and nothing, always the same hibernate exception... ;-(

    The worst is that I have used the same code inside a Macro and I it works withouth any problem...

    I already looking for on atlassian wiki, foruns and there is any place that could help me find a solution...

    Could somebody tell me if its a possible challenger before I give up on this?


    1. Hey Cristiano,

      it sounds like your Hibernate object are detached from the session and therefore when it tries to lazy load a collection it fails.

      I am not sure why it would happen right now. Any chance for you to create a forum entry and attach some code I could look at to possibly reproduce the issue and figure out what's happening?


      1. Hi Samuel, thanks for the answer...

        I've tried to give more details here: How to get access to Page.getChildren() inside a REST Confluence plugin?


  7. All good, now add a POST method example, ta!

    1. I found one example of using POST in REST for JIRA in
      which is a Fedex project.

      1. Yea, found that pretty soon after posting, the method arg incantation I needed was (@context HttpServletRequest req)

        1. @Context not @context I think.
          I wish we had a decent POST REST example, preferably including uploading XML. Some more info at

  8. Hi, 

    i did the tutorial and it worked fine.

    Now i am trying to migrate it to my own project. All i want to do is viewing a list of all users via a REST Service. but i stucked already with this question:

    Why does the REST stuff work with the refapp and not with a normal Jira on port 2990. I just changed these "variables" and once it worked and once not. What does the refapp have, and jira not? Are there probably some REST plugins missing, which are already installed on the refapp?

    Please give me some hints

  9. Hi,

    It is working good in jira 4.0 but it is not working on 4.3 and above versions.

    is it require to change anything/

    please help me on this


    1. I´m trying to do this tutorial  and I have the same problem. I´m working with Jira 4.2.

      Somebody could help me