In my last post, I walked through the initial steps of creating a well-architected, cross-product search plugin, including a look at the Atlassian plugin descriptor, various useful plugin module types and a few different reusable components provided by the plugin development platform.

However, I find myself feeling underwhelmed: our cross-product search plugin is not capable of search. It is a servlet that, despite its beautifully integrated user experience, contains no actual functionality. So let’s get back to work.

Step 1: REST basics

This is really where things start to get fun, and architecturally interesting. The approach we’ll take, and which we consider one of our most beneficial technical best practices, is to write a REST interface that will provide all of the functionality that our user interface will eventually need. There are a few points I want to address about this before getting started:

  • REST first: Based just on my own experience, I’d speculate that remote APIs are typically considered “nice to have” on a product roadmap much more frequently than they are considered “must have.” In other words, I’m guessing they’re most often an afterthought, even in our own products. That’s unfortunate, in my opinion, because I think a well-defined remote API — one that permits programmatic interaction with a substantial portion of a product’s functionality — is the key enabler for an ecosystem of user-built extensions (think “web 2.0 mashups,” which were super hot not too long ago) and consequently more ideas, more buzz and more business.The approach we’re taking here is sort of a riff on “Test First” from Extreme Programming; I’m calling it “REST first,” keeping my fingers crossed that I’m the first person clever enough to call it that. The idea is that if we can build a REST service for our application logic, that can do everything the UI needs with good runtime performance (measured primarily by network response time and total number of requests, given a certain server load), then we have a REST service that’s probably good enough for other people to write code against as well.
  • Three tiered architecture: Remember that time when it was the 1990s and it was still cool to talk about your data tier, your business logic and your presentation layer? Well, 20 years later it’s still sort of cool (n.b. “cool” is a relative term). This isn’t exactly the same thing, but we get similar benefits from architecting our plugin to have a clean separation between the user experience and underlying functionality. It keeps our code relatively spaghetti-free, lets us do all kinds of automated integration testing that we’d otherwise need some sort of browser automation for, helps us mentally formalize a complete state machine for our software’s functionality, and perhaps most importantly in the Atlassian development world, it helps us keep big chunks of our code isolated from the effects of the frequent and often undocumented changes that are made in the product APIs between versions. Of course writing a REST API is neither the only way nor the best way to achieve these things, necessarily, but if we’re going to write a REST API anyway, it’s worth keeping the benefits in mind.
  • What was all that Velocity stuff for? After I went to the trouble of setting up the Template Renderer in “Episode IV,” you’d figure I was preparing to render search results in Velocity on the server side. Pfft, 1998 called, they want their unnecessary page refreshes back. Velocity templates turn out to be a good, simple solution for some of what we want to do (such as setting an i18n’ed page title, as we saw), and when we come back to the UI side of the picture later in this tutorial, I’ll be doing a bit more work in Velocity. For the most part, however, I’ll want to do most of my work on the client side, using Ajax calls from jQuery, because everybody knows that more jQuery is synonymous with better user experience.

Okay, with all that out of the way, let’s get started. The plugin development platform provides a REST module type that provides a pre-configured Jersey (JAX-RS implementation) and Jackson (JSON processor). As usual, we just need to declare a dependency in the Maven POM and Atlassian plugin descriptor:

1 - simple rest source.png
The important attributes for the

1
<rest>

element are

1
path

and

1
version

: collectively with the host application’s context path they determine the base URI for all the REST resources provided by the plugin. I’ve specified a path of

1
/search-tutorial

and version of

1
1.0

, and the Plugin SDK is running my Confluence instance at

1
http://localhost:1990/confluence

, which means any resources we create will be addressable from a base path of

1
http://localhost:1990/confluence/rest/search-tutorial/1.0

.

I’ve also created a

1
SearchResource

class, using JAX-RS annotations, which of course still doesn’t do any searching whatsoever. It returns our traditional favorite, “hoho,” as a JSON string. One thing worth noting is that, for now, I’m using an Atlassian-specific annotation,

1
@AnonymousAllowed

, to let me access the resource without authentication, just for the sake of convenience. We can see this in action by requesting the resource we’ve defined. You can use any tool you want for this (curl or wget should work fine, for example) but I happen to use a little Java app, appropriately named RESTClient, since it knows how to format and syntax-highlight JSON. Behold, the fruits of our labor:

2 - simple rest response.png
Hoho. What’s next? Well, it’s not great to be constructing a JSON string manually; better would be to let Jackson do it for us, using its reflection magic to serialize an object into JSON syntax. While we’re at it, let’s not hardcode “hoho” into the object. Instead, we’ll set our resource up to accept a path parameter, and then have the response echo the parameter.

3 - simple representation source.png
There are a lot of annotations floating around now, some of which are JAX-RS standard and some of which are Jackson-specific. At this point, I’m going to be completely hand-wavey for the sake of brevity and just say “they’re pretty much self explanatory!” One thing worth pointing out is that I added the

1
@JsonCreator

annotation to my

1
SearchResultRepresentation

constructor, which lets Jackson reconstitute

1
SearchResultRepresentation

instances from JSON. We’re not using that functionality right now, and I don’t imagine we’ll ever have a reason to use it in production (since the JSON responses will only be consumed by the front end). The reason I’ve included it, and the reason we always include

1
@JsonCreator

constructors by convention, is for the sake of integration tests. We write our integration tests in Java primarily, so we want Jackson to be able to parse our JSON responses into Java objects for us. Similarly, the getter for the

1
echo

field isn’t strictly necessary (and my IDE complains to me that

1
getEcho()

is unused), but I’ve included it by convention for testing.

Annotations and convention aside, the code should be pretty easy to follow. Our

1
get()

method takes a parameter, which is used in the representation object’s constructor. The representation is returned in the

1
ok()

response, serialized to JSON automatically. Using RESTClient, the request and response now look something like…

4 - simple representation response.png

Step 2: Search (really, finally)

Okay, we’ve finally got enough of an architectural framework built, on top of which we could probably implement any plugin we wanted to, more or less. I’m actually really eager right now to start illustrating how similar the “plumbing” we’ve built so far is to the architecture of other real-world plugins… But I’ll keep it to myself for now, and focus on getting this search functionality actually working, finally.

One of the most important components in the Atlassian plugin development platform is a cross-product API called SAL, the Shared Access Layer. It contains many useful services that are (for the most part) implemented in each Atlassian product. We’ll be using several services from SAL in this tutorial, starting with

1
<a href="http://docs.atlassian.com/sal-api/2.2.1/sal-api/apidocs/com/atlassian/sal/api/search/SearchProvider.html">SearchProvider</a>

.

Just like in “Episode IV,” we need to declare a dependency in the Maven POM, declare a

1
<a href="http://confluence.atlassian.com/display/PLUGINFRAMEWORK/Component+Import+Plugin+Module">component-import</a>

in the plugin descriptor, and inject the

1
SearchProvider

into an appropriate object’s constructor:

5 - sal source.png
This pattern of code reuse, consisting of injecting an imported component, is one that pops up frequently in Atlassian plugin development. More sophisticated plugins, in addition to using components provided by the platform, also provide their own components (and even new plugin module types) to other plugins.

One thing I should probably point out here is that I chose to inject the

1
SearchProvider

object into the

1
SearchResource

constructor, rather than into

1
SearchResultsRepresentation

. Ignoring the fact that it’s good design to keep the REST representation objects separate from the application logic, it also wouldn’t work. You can only inject dependencies into objects constructed by the plugin system, not into objects you construct yourself using

1
new

.

1
SearchResource

is constructed by the plugin system (by way of the REST module), but

1
SearchResultsRepresentation

is not.

One last thing before moving on:

1
SearchResultsRepresentation

now has two constructors: one that’s a

1
@JsonCreator

, like we had before, and a new one that takes a SAL

1
<a href="http://docs.atlassian.com/sal-api/2.2.1/sal-api/apidocs/com/atlassian/sal/api/search/SearchResults.html">SearchResults</a>

instance. This is a very common pattern: all REST representation objects typically have a

1
@JsonCreator

-annotated constructor, with parameters identical to the class’s fields, and a regular constructor that takes an instance of the domain object(s) that the representation is based on.

Okay, let’s smoketest this:

6 - sal response.png

Step 3: More RESTful, More SAL

Nothing crashed, good enough. Before we finish up, and add representation objects corresponding to the interesting bits of

1
SearchResults

, there’s a small but important bit of technical debt we need to address. We’re writing a REST service, but thus far we’ve neglected an important design principle in REST API design: HATEOAS. Yeah, it’s an awkward name for a design principle, and I personally have no idea how to pronounce it, but it’s important nonetheless: we want clients of our API to have only one URL baked in, from which all other resources in our REST API are transitively reachable. This principle simultaneously reduces unnecessary coupling between client and server, and makes it much easier for the client to access the resources it’s likely to need.
The convention we use at Atlassian is to include a map of links, including a “self” link, in the representations returned from all REST resources. Let’s get this set up:

7 - links source.png
Okay, I did a few things at once there:

  • I needed a “self” link in my representation object, so I’ve added one to the regular constructor (and also added the links map in the
    1
    @JsonCreator

    constructor).

  • I needed to populate the “self” link somehow, so I’ve used a JAX-RS
    1
    <a href="http://jersey.java.net/nonav/apidocs/latest/jersey/javax/ws/rs/core/UriBuilder.html">UriBuilder</a>

    implementation from Jersey (rather than, say, string concatenation).

  • The
    1
    UriBuilder

    needed some base URL, which I can get from another SAL service,

    1
    <a href="http://docs.atlassian.com/sal-api/2.2.1/sal-api/apidocs/com/atlassian/sal/api/ApplicationProperties.html">ApplicationProperties</a>

    .

  • The
    1
    ApplicationProperties

    object needs to come from somewhere, so I declared another

    1
    component-import

    and injected it into the

    1
    SearchResource

    (along with the

    1
    SearchProvider

    ).

In a bigger codebase, I would be more inclined to keep this stuff separate from my REST resource objects; I’d probably factor it out into some sort of

1
LinksBuilder

just to keep the resources clean. But for the purposes of this tutorial, considering we have only one resource, I was lazy.

Another quick smoketest:

8 - links response.png
Decent. Of course, if there were more REST resources in the plugin, we’d want to link those as well, but this is good for now.

Step 3&frac12;: Losing my Battle with OCD

One final bit of cleanup, before we finish fleshing out the results: although we’ve been really good so far about maintaining a clean macro-level architecture, we’ve been a bit lazy about micro-level code quality — things like null checks and mutable collections — the sorts of things that a static analysis tool might warn about. For that, we like to use Google’s core Java libraries:

9 - google collections source.png
Making preconditions explicit results in safer code, and making our data immutable wherever possible results in both safer and faster code, particularly when concurrency comes into play. The Google libraries do a nice job of providing these, while contributing to readability (e.g.

1
ImmutableMap.of(...)

is much better than

1
Collections.unmodifiableMap(new HashMap(...))

).

Step 4: The Coup de Grâce

We like to say that we spend 90% of our time, as Java web developers, transforming collections. SAL’s

1
SearchResults

contains a collection of search matches and a collection of error messages that we need to transform into some useful REST representation. Let’s do this.

10 - authentication needed.png
First off, I’ve added representation classes for search matches and errors (empty for now), and shuffled the package structure around a bit, just to keep things clean. I’ve also finally removed the

1
@AnonymousAllowed

annotation on the

1
get()

method, since to get any useful search results, you do actually need to be an authenticated user. To find out the username for the current logged in user, I’m using yet another SAL service,

1
<a href="http://docs.atlassian.com/sal-api/2.2.1/sal-api/apidocs/com/atlassian/sal/api/user/UserManager.html">UserManager</a>

, which I’ve imported and injected the usual way. Nothing too fancy yet…

But now, it’s time to…

11 - transforming collections.png
TRANSFORM COLLECTIONS LIKE A BOSS. Okay, if you haven’t seen this style of code before, you’ll probably want to take a couple minutes to stare at it. Here’s the deal: we’re nerds, we like functional programming, and we hate

1
for

loops. So, rather than iterating through the list of matches in the

1
SearchResults

, constructing a new

1
SearchMatchRepresentation

for each one, and adding it to a list, I’m using the Google common libraries to do the transformation all in one shot. I actually would have been much happier to write this whole thing in Scala, because I think transforming collections (and pretty much everything else) is just much more pleasant in it, but I don’t think that would have gone over well here.

I’ll just say: you totally don’t have to write your code this way, but I’ll be proud of you if you do.

One last thing I should mention about the

1
SearchMatchRepresentation

and

1
SearchErrorRepresentation

classes: they aren’t really for standalone use. In other words, they don’t correspond to individual, addressable REST resources. They’re just aggregate bits of the

1
SearchResultsRepresentation

, and as such, they don’t contain their own individual links map.
Enough delayed gratification, let’s see what we’ve ended up with:

12 - search response.png
I’m satisfied. We’ve got a search term in the resource URI, we’re authenticated, and we’ve got an actual search result in our RESTClient. Still no user interface to speak of, and still not cross-platform, but our search plugin finally has some actual search functionality. Huge success.

Summary

The main topic I covered in this post was the Atlassian REST module, including:

  • Reasons to build your plugin “REST-first,”
  • A little bit about REST API design principles,
  • Conventions and idioms when using Jersey and Jackson,
  • The RESTClient test utility.

We also covered a few components from SAL that are useful in many different plugins, and some functional-style techniques for making your code faster and more bulletproof. In “Episode VI,” we’ll finally write a user interface for the search plugin, tying together the work we’ve done so far.

Again, I’ve made the code for this iteration available for download. And as before, if you like this tutorial and you want to see more, please let us know!