Last updated Jul 12, 2024

Stateless web-resource transforms and conditions


Atlassian Plugin Framework 3.0 and later.

From Bitbucket 6.8, Confluence 6, and Jira 7 transforms and conditions should not assume that they have access to the cookie or current user when serving CSS or JavaScript resources.

To achieve this, we have created new APIs for web-resource transforms and conditions. This page describes how to update your transforms and conditions to these new APIs.

The core problem

Delivering web-resource batches (CSS + JavaScript) to a page is a two-stage process:

  1. page-render-time: when rendering the HTML page, calculate and render the URLs for CSS <link> and JavaScript <script> tags.
  2. resource-serve-time: when serving those CSS + JavaScript resources, calculate the required resources in those batches and send them to the client.

At resource-serve-time, we process web-resource conditions and transforms. Previously, these conditions and transforms have had access to the user's cookie -- for example, the i18n transform (that renders internationalised strings in JavaScript resources) can look at the cookie on the incoming request and determine that user's locale.

Under the new APIs, all the information required by the transformer or condition must be encoded in the URL. Do not assume that you will have a current user at resource-serve-time. To achieve this, we have deprecated the old web-resource transform and condition APIs and created new APIs:

  • for transforms, com.atlassian.plugin.webresource.transformer.WebResourceTransformer has been deprecated in favour of com.atlassian.plugin.webresource.transformer.WebResourceTransformerFactory.
  • for conditions, the use of com.atlassian.plugin.web.Condition in web-resources has been deprecated in favour of com.atlassian.plugin.webresource.condition.UrlReadingCondition. (Note that the Condition class itself is not deprecated, as it is still relevant for web-fragments as opposed to web-resources.)

These APIs appear in atlassian-plugins-webresources version 3.0.

How do I fix offending transforms?

There are two cases to fix here:

Transforms that do compilation

Examples include LESS and i18n transformation.

To fix these, convert from WebResourceTransformer to WebResourceTransformerFactory (which produces UrlReadingWebResourceTransformers). This enables you to contribute to the URL at page-render-time and read from that URL at resource-serve-time.

Taking the i18n transform as an example:

Note that fixing the transform can be hard, as it may not be the transform class that is actually doing the cookie lookup; it may be something deeper in the dependency-injection hierarchy. For this reason, it is better to convert to the data API if possible (see below).

Transforms that do variable injection

For a long time, there was no good way of inserting JSON data into a page. One solution people used was to use transforms to inject data into JavaScript as it is being served -- for example, the ContextPathTransformer. Now, there is a much better way -- deliver JSON data via the Stateless web-resource transforms and conditions.

To see this in action, see the new ContextPathProvider. This consists of three parts:

How do I fix offending conditions?

When looking at conditions, the first thing to consider is:

Do you really need a condition here? What would be the cost of delivering the JavaScript / CSS resource? It makes little difference to performance if we serve a 1 or 2 extra KB.

If you do need to convert it, the most common solution is to convert your conditions to a UrlReadingCondition.

Note one potential issue:

  • UrlReadingConditions are included in the batch. This fixes a long-standing bug where resources with conditions are delivered as separate resources, breaking web-resource dependency order (and making dependency different between dev and prod mode). However, there is a strong chance that your code (especially CSS) was written knowing that this bug existed. You should test to ensure that this change in batch ordering has not affected your plugin.

Dual compatibility

You only need to follow these steps if you're supporting versions older than Bitbucket 6.8, Confluence 6, and Jira 7.

If your plugin targets products using old versions of web-resource-manager but you want to make use of the transform2 / condition2, use the following techniques to be compatible:


Keep your transform1 implementation untouched and add a transform2 declaration with alias-key = the key of the transform1.

The transform lookup algorithm for a key my-key is as follows:

  • Look for a transform2 with key = my-key
  • If not found, look for a transform2 with alias-key = my-key
  • If not found, look for a transforms1 with key = my-key
<!-- in the plugin declaring the transform -->
<web-resource-transformer key="jsI18n" name="JavaScript I18n Transformer" class="com.atlassian.aui.javascript.JsI18nTransformer" />
<url-reading-web-resource-transformer key="jsI18n-2" alias-key="jsI18n" name="JavaScript I18n Transformer" class="com.atlassian.aui.javascript.UrlReadingJsI18nTransformer" />
<!-- in a plugin using the transform -->
<web-resource key="my-resource">
  <transformation extension="js">
    <transformer key="jsI18n" />

In an old product, the <url-reading-web-resource-transformer> will be ignored.


Add a class2 attribute to your <condition> declaration:

<web-resource key="my-resource">
    <param name="property"></param>

In an old product, class2 will be ignored.

In a new product, for a web-resource only (ie not a web-item / web-panel etc), the contract is to look for class2 if it exists, otherwise use class.

Note that class can be either a condition1 or a condition2 - in particular if you're implementing something for condition2 only, class can be a condition2.

Rate this page: