Last updated Mar 27, 2024

Developing a REST service plugin

Applicable:

This tutorial applies to RefApp 3.3.6

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.

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:

1
2
git clone git@bitbucket.org:serverecosystem/message.git

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

bitbucket.org/serverecosystem/message

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:

    1
    2
    atlas-create-refapp-plugin
    
  3. When prompted, enter the following values for your plugin project parameters:

    group-id

    com.atlassian.plugins.tutorial

    artifact-id

    Message

    version

    1.0

    package

    com.atlassian.plugins.tutorial

  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:

    1
    2
    atlas-create-refapp-plugin-module
    
  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: com.atlassian.plugins.tutorial.rest
    • 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: 

1
2
<project_home>/src/main/java/com/atlassian/plugins/tutorial/rest/

This directory contains two source files that implement the REST services: 

  • MyRestResource.java 
  • MyRestResourceModel.java

Open the files to have a look. Notice the contents of MyRestResource.java. 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 MyRestResource.java

Annotation

Description

@Path

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.

Source: Path

Scope: Class or Method

@GET

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: javax.ws.rs.

Source: GET

Scope: Method

@AnonymousAllowed

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.

Scope: Method

@Produces

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.

Source: Produces

Scope: Method

@XmlRootElement

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

Source: XmlRootElement

Scope: Class or enum

@XmlAccessorType

Controls whether fields or properties are serialised by default.

Source: XmlAccessorType

Scope: Class or enum

@XmlAttribute

Maps a property or field to an XML Attribute.

Source: XmlAttribute

Scope: Property or field

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:

1
2
atlas-run

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: 

http://<host>:5990/refapp/rest/myrestresource/1.0/message

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

You should see a page with text that resembles:

1
2
This XML file does not appear to have any style information associated with it. The document tree is shown below.
<message key="default">
    <value>Hello World</value>
</message>

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 MyRestResource.java for editing.

  2. Replace the getMessage() method with this:

    1
    2
    public Response getMessage(@QueryParam("key") String key)
    {
       if(key!=null)
          return Response.ok(new MyRestResourceModel(key, getMessageFromKey(key))).build();
       else
          return Response.ok(new MyRestResourceModel("default","Hello World")).build();
    }
    
  3. Add two new methods, one of which takes a path parameter and another that takes a query parameter.

    1
    2
    @GET
    @AnonymousAllowed
    @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
    @Path("/{key}")
    public Response getMessageFromPath(@PathParam("key") String key)
    {
       return Response.ok(new MyRestResourceModel(key, getMessageFromKey(key))).build();
    }
    
    private String getMessageFromKey(String key) {
       // In reality, this data would come from a database or some component
       // within the hosting application, for demonstration purpopses I will
       // just return the key
       return key;
    }
    
  4. Open MyRestResourceModel.java and add a new element declaration:

    1
    2
    @XmlAttribute
    private String key;
    
  5. Replace the class constructor with this one:

    1
    2
    public MyRestResourceModel(String key, String message) {
       this.key = key;
       this.message = message;
    }
    
  6. Finally, add two new methods to the class:

    1
    2
    public String getKey() {
       return key;
    }
     
    public void setKey(String key) {
       this.key = key;
    }
    

Notice we've added a few more annotations: 

Annotation

Description

@XmlElement

Maps a property or field to an XML Element.

Source: XmlElement

Scope: Property or field

@PathParam

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

Source: PathParam

Scope: Method Parameter

@QueryParam

It maps a method variable to a query parameter.

Source: QueryParam

Scope: Method 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're passing in an id to find some information about some sort of entity, then use a path parameter.

On the other hand, if your input is something that might be changed 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:

    1
    2
    <project_home>/src/test/java/ut/com/atlassian/plugins/tutorial/rest 
    
  2. Open MyRestResourceTest.java 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: 

    1
    2
     Response response = resource.getMessage("Hello World");
    

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: 

    1
    2
    atlas-run
    
  2. In a web browser, open the RefApp application:
    http://<host>:5990/refapp/

  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 left side menu under RESTAPI DEV.
    The browser shows one of the services exposed by the application, along with the resources in that service. 

  6. Uncheck Show only public APIs and search using the term message.

  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:

Rate this page: