1. Atlassian
  2. Project: Atlassian
  3. atlassian-pretty-urls

Overview

Atlassian Pretty URLs

Description

Atlassian Pretty URLs allows a plugin to map from one URL to another. It allows basic rewriting of URLs at request time.

Atlassian Developer?

Committing Guidelines

Please see The Platform Rules of Engagement (go/proe) for committing to this module.

Builds

The Bamboo builds for this project are on EcoBAC.

Local development

Run tests in refapp: mvn verify

Run up the reference plugin in refapp and allow attaching debugger:

mvn -DskipTests clean install mvn -pl atlassian-pretty-urls-reference-plugin/ -DskipTests -Djvm.debug.suspend=true amps:debug

External User?

Issues

Please raise any issues you find with this module in JIRA.

Documentation

See below

Do you despise your plugin URLs?

How To Deploy It For Your Plugin

Put this in your pom.xml

<dependency>
    <groupId>com.atlassian.prettyurls</groupId>
    <artifactId>atlassian-pretty-urls-plugin</artifactId>
    <version>1.4</version>
    <scope>provided</scope>
</dependency>

You will then need to OBR bundle the pretty urls plugin along with your bundle.

<plugin>
   <groupId>com.atlassian.maven.plugins</groupId>
   <artifactId>maven-jira-plugin</artifactId>
   <version>${amps.version}</version>
   ...
   <!-- These go into the OBR -->
   <pluginDependencies>
      <pluginDependency>
         <groupId>com.atlassian.prettyurls</groupId>
         <artifactId>atlassian-pretty-urls-plugin</artifactId>
      </pluginDependency>
   </pluginDependencies>
</plugin>

The Plugins system does not have full OBR support yet (eg AMPS cannot deploy your OBR into its started JIRA via copying it to installed-plugins directory) so you need to manual put the extra plugin in play via AMPS pluginArtifact copying

<plugin>
   <groupId>com.atlassian.maven.plugins</groupId>
   <artifactId>maven-jira-plugin</artifactId>
   <version>${amps.version}</version>
   ...
   <pluginArtifacts>
      <pluginArtifact>
         <groupId>com.atlassian.prettyurls</groupId>
         <artifactId>atlassian-pretty-urls-plugin</artifactId>
      </pluginArtifact>
   </pluginArtifacts>
</plugin>

How To Make URLs Pretty

Simple Routing

The Atlassian Pretty URLs plugin allows you to facade any old style URL into one of your choosing. You do this by adding <route> elements to your atlassian-plugin.xml

<routing key="prettyurls-key" path="/prettyurls">
    <route from="/are/{verb}" to="/secure/HorribleName.jspa"/>
    <route from="/can/{verb}" to="/secure/HorribleName.jspa"/>
</routing>

The first entry above will map

http://localhost:2990/jira/prettyurls/are/cool ==> http://localhost:2990/jira/secure/HorribleName.jspa?verb=cool

URL Template Parameters

It uses jax-rs syntax to take URL path parameters and make them available to the destination as either further jax-rs variables or more commonly as query parameters. Any left over parameters that are not remapped into the destination URL are passed as query parameters. For example :

http://localhost:2990/jira/prettyurls/are/cool?leftover=parameters&are=passedon ==> http://localhost:2990/jira/secure/HorribleName.jspa?verb=cool&leftover=parameters&are=passedon

You can use the template variables within the destination URL and hence allow more dynamic routing. For example given

<routing key="prettyurls-key" path="/prettyurls">
    <route from="/hello{world}/{id}" to="/plugins/servlet/hello{world}?idParameter={id}"/>
</routing>

you could end up mapping to URL dynamically to differently named servlets as shown below.

http://localhost:2990/jira/prettyurls/helloCleveland/key123 ==> http://localhost:2990/jira/plugins/servlet/helloCleveland?idParameter=key123

http://localhost:2990/jira/prettyurls/helloSydney/key456 ==> http://localhost:2990/jira/plugins/servlet/helloSydney?idParameter=key123

You can find further examples of what is possible with URL Templates by reading the Oracle documentation.

HTTP Verbs

You can limit the HTTP verbs upon which routing will take place via the verbs attribute. For example :

<routing key="prettyurls-key" path="/prettyurls">
    <route from="/http/get" to="/secure/HorribleName.jspa" verbs="get"/>
    <route from="/http/post" to="/secure/HorribleName.jspa" verbs="post"/>
    <route from="/http/post_put_or_delete" to="/secure/HorribleName.jspa" verbs="post,put,delete"/>
</routing>

There is also syntactic sugar for the well known HTTP verbs get, put, post, head, delete, options and patch.

<routing key="prettyurls-key" path="/prettyurls">
    <get from="/http/sugar/get" to="/secure/HorribleName.jspa"/>
    <post from="/http/sugar/post" to="/secure/HorribleName.jspa"/>
</routing>

Filter Chain Location

You can control when the routing happens via the location attribute. The valid values are before-decoration, before-login and before-dispatch. The most useful location is before-dispatch.

<routing key="prettyurls-ref-rest-after-encoding" path="/prettyjson" location="before-dispatch">
    <route from="/" to="/rest/prettyjson/1.0/json"/>
</routing>

<routing key="prettyurls-ref-before-login" path="/before/login" location="before-login">
    <route from="/are/{verb}" to="/secure/PrettyActionUrlsActionHorribleName.jspa"/>
</routing>

<routing key="prettyurls-ref-before-decoration" path="/before/decoration" location="before-decoration">
    <route from="/get/allowed" to="/secure/PrettyActionUrlsActionHorribleName.jspa" verbs="get"/>
    <route from="/get/post/allowed" to="/secure/PrettyActionUrlsActionHorribleName.jspa" verbs="get,post"/>
</routing>

Multiple Top Level Routing Matching via JAX-RS

Its possible that two or more consumer plugins can compete for the same top level path

<routing key="x" path="/prettyurl"">
    <route from="/some/path" to="/secure/SomePath.jspa"/>
    <route from="/some/other/path/{with}/{variable}" to="/secure/SomeOtherPath.jspa"/>
</routing>

<routing key="y" path="/prettyurl"">
    <route from="/some/{all : .*}" to="/secure/IWantEverything.jspa"/>
</routing>

In this case we need a resolution strategy. jax-rs has a strategy for this : jax-rs Request Matching

Sort [...] using the number of literal characters in each member as the primary key (descending order), 
the number of capturing groups as a secondary key (descending order) and the number of capturing groups 
with non-default regular expressions (i.e. not ‘([^  /]+?)’) as the tertiary key (descending order).

In short the more specific and longer paths will be matched first. So when multiple routing elements are competing for the same top level path then jax-rs matching is used. In the case of a single routing elements then list order is used.

In the config give above we would see :

  • /some/path would be routed to /secure/SomePath.jspa
  • /some/other/path/including/variables would be routed to /secure/SomeOtherPath.jspa
  • /some/thing-else would be routed to IWantEverything.jspa
  • /some/thing-more would be routed to IWantEverything.jspa

Routing REST Requests

If you want to route to REST resources, then you need to tweak your <rest> module type declaration to ensure it contains the FORWARD dispatcher attribute. The reason for this is that the atlassian-pretty-urls plugin uses request forwarding as its implementation and as such the REST resources must be mapped to the FORWARD dispatcher context. The default <rest> dispatcher context is REQUEST only and hence it will never match up as expected without this configuration.

<rest key="prettyurls-ref-rest" path="/prettyjson" version="1.0">
    <package>com.atlassian.prettyurl</package>

    <!--In order to allow REST calls to be intercepted your MUST include a FORWARD call here-->
    <dispatcher>REQUEST</dispatcher>
    <dispatcher>FORWARD</dispatcher>
</rest>

How To Make Pretty URLs Decorated

JIRA does not SiteMesh decorate all URL paths (and nor should it) so you have to tell it if you want a path decorated. You can do this via the <sitemesh> module type.

<sitemesh key="unique-key-for-module-type" path="/pluginpath"/>

This will cause all text/html responses to be SiteMesh decorated.