Struts 6.3 upgrade in Confluence 8.6

Hi all,

We’ve upgraded our MVC framework from Struts 2.5 to Struts 6.3 in Confluence 8.6.

If your plugin targets an earlier version of Confluence, we recommend using confluence-compat-lib to call ActionContext and ServletActionContext methods.

We recently released version 1.6.0 which added support for Struts 6.3 as well as the following fixes:

  • Fixed #getParameters not being consistent between WebWork and Struts2
  • Implemented auto-initialisation of ActionContext when using #setContextMap
  • Enforced type safety where appropriate

This library now provides transparent compatibility between all currently supported versions of Confluence, including:

  • Confluence 7.19 LTS (WebWork 2.1)
  • Confluence 8.5 LTS (Struts 2.5)
  • Confluence 8.6 (Struts 6.3)

More details on how to use this library can be found in this guide.

As part of this upgrade, we’ve additionally implemented the following stricter security measures:

  • Proxied objects (e.g. Spring AOP) cannot be accessed from OGNL expressions
  • Static fields cannot be accessed from OGNL expressions
  • Default package classes cannot be accessed from OGNL expressions
  • OGNL expressions are limited to 150 characters
  • OGNL expressions cannot contain certain nodes/operations
  • Stricter naming requirements for actions ([a-zA-Z0-9\-]*), action methods ([a-zA-Z_]*[0-9]*), and namespaces ([a-zA-Z0-9/\-]*)
  • OGNL ($ognl) and Struts ($struts) objects removed from Velocity context

The upcoming EAP will contain most of the aforementioned changes, with the remainder to be progressively implemented prior to final release.

Kusal Kithul-Godage
Software Engineer, Confluence Data Center


Hey @Kusal ,

I’m having difficulty on 8.6.0-m79 with the Struts upgrade. I’ve upgraded the compat lib to 1.6.0 and everything is working fine on earlier versions, but not for 8.6.0.

Our simple action is as follows:

 <xwork name="SR Confluence Space Admin Action" key="space-admin-action">
        <description>Action for SR Confluence Space Admin</description>
        <constant name="struts.devMode" value="true" />
        <package name="sr-conf-space-admin" extends="json-default, velocity-default" namespace="/scriptrunner-confluence/space_admin">
            <default-interceptor-ref name="validatingStack"/>
            <action name="items"
                <result name="success" type="velocity">/com/onresolve/scriptrunner/templates/vm/space-admin-items.vm
                <result name="error" type="velocity">/com/onresolve/scriptrunner/templates/vm/space-admin-error.vm

Invoking the action through the url:


Results in this error:

[apache.struts2.dispatcher.Dispatcher] Could not find action or result: /confluence/scriptrunner-confluence/space_admin/items.action?section=space_admin_builtin_scripts&key=ds
There is no Action mapped for namespace [/] and action name [items] associated with context path [/confluence]. - [unknown location]

I can’t seem to figure out what would need to be changed on our end. It looks like there would need to be some changes to Confluence’s web.xml/struts.xml. Can you let me know if I’m missing something?


Apologies @jyamdogo, there was a typo in my original post. Namespaces must now meet the RegEx validation [a-zA-Z0-9/\-]* - underscores are no longer permitted. Hope that helps.

So the plugin key can’t be used anymore for namespaces? The dot is not permitted anymore?

I thought it was documented somewhere that plugins were required to be deployed under /admin/{pluginKey}.

Hi @aragot

I understand that some of our tutorials and code examples have used the artifactId for the Struts namespace; although, I don’t believe that has ever been a requirement. Please let me know if you have found documentation stating otherwise and I will look into this further.

It is however definitely recommended to use a unique namespace name to avoid conflicting with other plugins.

And yes, periods are also now prohibited from namespaces.

The requirement of having apps under the namespace /admin/plugins/{appKey} is not here anymore, that’s correct. There was a reference to it in 2014 when the Marketplace was launched. Maybe the requirement was removed years ago.

Maybe, if you have the edit permissions, you can remove it from this example on this page as well:

1 Like

@Kusal , In your Strust 2 upgrade guide, I see that $req is deprecated in favour of $request, with the mention “For velocity VM files only. For now, we have put $req for backward compatibility, we might clean this up by end of 8.0” As you know, overwhelming cases of using this variable are for the context path: <a href="${req.contextPath}/my-link.action?..."

Can we already request that the duration of this backward compatibility is for two years after the next LTS? Here’s why:

  • App developers need to support customers,
  • Atlassian tells customers that LTS versions are supported for two years,
  • So, if Confluence 8.8 is LTS and published in December 2023, our plugins need to be compatible with it until December 2025, or it would be bad experience for customers,
  • Until then, we can’t use $request because it hasn’t been introduced for some customers, and we need to keep using $req for older customers.

I understand that the full $request object may be too hard for you to inject; A dummy object with ${req.contextPath} only would be satisfying. We are able to replace all other occurrences of $req with conditionals, but the case of contextPath is hairy because it is used extremely often, and a compatibility conditional in this position would be verbose.

Instead of working through the differences related to $req/$request with conditional logic in a template you may want to consider injecting the context path into the template render context yourself. In your Java code used to render the template you could use your own custom parameter:

// Backwards-compatible way to provide context path in the velocity template.
// Confluence 7.x uses $req and Confluence 8.x uses $request in velocity templates so
// we step around that incompatibility by providing our own custom parameter:
context.put("reqContextPath", request.getContextPath());

Then consume your custom $reqContextPath in your velocity template.

Dear @Kusal

may I ask where you published version 1.6.0 of the confluence-compat-lib? It is not here.

Thank you.


It looks like Atlassian’s closed-source artifacts are no longer indexed on They are still accessible via the Atlassian Maven proxy.

1 Like

Thank you for your contributions @turehoefner_appfire and @scott.dudley , I’ve opted for this syntax (rather than #set ($req = $request)) to avoid overriding $req if another plugin defines $request and still override it when Confluence doesn’t define $req anymore:

#if (!$req) #set ($req = $request) #end
1 Like