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. |
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.
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 2git clone git@bitbucket.org:serverecosystem/message.git
Alternatively, you can download the source using the get source option here:
bitbucket.org/serverecosystem/message
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.
Open a terminal and navigate to the directory where you want to create your REST plugin project home directory.
Enter the following command:
1 2atlas-create-refapp-plugin
When prompted, enter the following values for your plugin project parameters:
group-id |
|
artifact-id | Message |
version |
|
package |
|
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.
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:
Change to the new Message
directory.
Run the following command:
1 2atlas-create-refapp-plugin-module
Enter 7, REST Module Plugin.
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:
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.
Enter N when prompted to show advanced setup options.
Enter N when asked whether you'd like to add another plugin module.
The SDK finishes up, adding REST source files to the project.
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 |
---|---|
| Identifies the URI path that a resource class or class method will serve requests for. Source: Path Scope: Class or Method |
| 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 |
| 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 |
| 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 |
| Maps a class or an enum type to an XML element. Source: XmlRootElement Scope: Class or enum |
| Controls whether fields or properties are serialised by default. Source: XmlAccessorType Scope: Class or enum |
| 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.
Start the RefApp using the following SDK command. Thanks to some SDK magic, your plugin is loaded in the application automatically:
1 2atlas-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 2This 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.
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.
Open MyRestResource.java
for editing.
Replace the getMessage()
method with this:
1 2public 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(); }
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; }
Open MyRestResourceModel.java
and add a new element declaration:
1 2@XmlAttribute private String key;
Replace the class constructor with this one:
1 2public MyRestResourceModel(String key, String message) { this.key = key; this.message = message; }
Finally, add two new methods to the class:
1 2public String getKey() { return key; } public void setKey(String key) { this.key = key; }
Notice we've added a few more annotations:
Annotation | Description |
---|---|
| Maps a property or field to an XML Element. Source: XmlElement Scope: Property or field |
| It maps a method variable to an element in the @Path. Source: PathParam Scope: Method Parameter |
| 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.
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:
Navigate to the test directory for the plugin:
1 2<project_home>/src/test/java/ut/com/atlassian/plugins/tutorial/rest
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.
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 2Response response = resource.getMessage("Hello World");
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.
Start the RefApp again:
1 2atlas-run
In a web browser, open the RefApp application:
http://<host>:5990/refapp/
Log in with the default username and password, admin/admin.
Click on the username link at the top right, A.D.Ministrator.
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.
Uncheck Show only public APIs and search using the term message.
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.
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.
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.
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: