Update: We are backporting the mentioned changes to Confluence 8.5 LTS immediately
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.1 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)
The library now requires the following additional package import:
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"
class="com.onresolve.scriptrunner.confluence.action.ConfluenceSpaceAdminAction"
method="fetchItems">
<result name="success" type="velocity">/com/onresolve/scriptrunner/templates/vm/space-admin-items.vm
</result>
<result name="error" type="velocity">/com/onresolve/scriptrunner/templates/vm/space-admin-error.vm
</result>
</action>
</package>
</xwork>
[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.
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.
@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.
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:
after updating confluence-compat-lib from version 1.5.4 to 1.6.0 we’ve seen that class XStreamManagerCompat is not available anymore, is there any fix or solution?
Err…but doesn’t that defeat the whole purpose of using confluence-compat-lib? What if vendors want to support both Confluence 7.9 and Confluence 8.6 with the same JAR? Can this class be reinstated? (Don’t forget that vendor version support matrices are not necessarily the same as Atlassian’s.)
After upgrading the lib to version 1.6.1, using Confluence 8.6.0-rc1, after installing the app, when trying to use it from Space tools option, we get the following error/warning messages:
[opensymphony.xwork2.ognl.SecurityMemberAccess] isAccessible Access to non-public [private com.atlassian.plugin.webresource.WebResourceManager com.comalatech.confluence.workflowcontainer.action.SpaceWorkflowsAction.webResourceManager] is blocked!
Also please see attached stacktrace.pdf file that includes the following error:
The first error is as stated, you must add a public getter for any private fields you wish to access via expressions.
To resolve the second error you must add the following additional package import to your plugin: com.opensymphony.xwork2.conversion.impl;resolution:=optional,
I will look into amending the relevant changelog and documentation as I can see both these points have been missed.
We are using the confluence-compat-lib 1.6.1 and try to put a key/value pair into actionContextCompatManager.getSession().
Unfortunately, calling getSession does not work in Confluence 8.6:
com.atlassian.confluence.api.service.exceptions.ServiceException: Struts6 ActionContext couldn't be initialized. at com.atlassian.confluence.compat.struts2.actioncontext.ActionContextCompatManager.initialiseActionContextCompat(ActionContextCompatManager.java:49) at com.atlassian.confluence.compat.struts2.actioncontext.ActionContextCompatManager.lambda$new$0(ActionContextCompatManager.java:31) at com.google.common.base.Suppliers$NonSerializableMemoizingSupplier.get(Suppliers.java:181) at com.atlassian.confluence.compat.struts2.actioncontext.ActionContextCompatManager.getSession(ActionContextCompatManager.java:234)
(…)
Caused by: java.lang.ClassNotFoundException: com.opensymphony.xwork2.conversion.impl.ConversionData not found by net.seibertmedia.xxxxxxxx [309] at org.apache.felix.framework.BundleWiringImpl.findClassOrResourceByDelegation(BundleWiringImpl.java:1591) at org.apache.felix.framework.BundleWiringImpl.access$300(BundleWiringImpl.java:79) at org.apache.felix.framework.BundleWiringImpl$BundleClassLoader.loadClass(BundleWiringImpl.java:1976) at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:525) at java.base/java.lang.Class.forName0(Native Method) at java.base/java.lang.Class.forName(Class.java:467) at com.atlassian.confluence.compat.struts2.actioncontext.ActionContextStruts6.<init>(ActionContextStruts6.java:47) at com.atlassian.confluence.compat.struts2.actioncontext.ActionContextCompatManager.initialiseActionContextCompat(ActionContextCompatManager.java:47)
Are we doing something wrong here or is it an issue with Confluence 8.6 or the confluence-compat-lib? @Kusal
When using confluence-compat-lib 1.5.4, the error is different, but still exists (which is no surprise because of the Struts upgrade in Confluence 8.6):
com.atlassian.confluence.api.service.exceptions.ServiceException: ActionContext couldn't be initialized. at com.atlassian.confluence.compat.struts2.actioncontext.ActionContextCompatManager.initialiseActionContextCompat(ActionContextCompatManager.java:45) at com.atlassian.confluence.compat.struts2.actioncontext.ActionContextCompatManager.lambda$new$0(ActionContextCompatManager.java:29) at com.google.common.base.Suppliers$NonSerializableMemoizingSupplier.get(Suppliers.java:181) at com.atlassian.confluence.compat.struts2.actioncontext.ActionContextCompatManager.getSession(ActionContextCompatManager.java:205)
(…)
Caused by: java.lang.NoSuchMethodException: com.opensymphony.xwork2.ActionContext.setApplication(java.util.Map) at java.base/java.lang.Class.getMethod(Class.java:2227) at com.atlassian.confluence.compat.struts2.actioncontext.ActionContextStruts2AndWWCompat.getACStruts2Method(ActionContextStruts2AndWWCompat.java:229) at com.atlassian.confluence.compat.struts2.actioncontext.ActionContextStruts2AndWWCompat.<init>(ActionContextStruts2AndWWCompat.java:38) at com.atlassian.confluence.compat.struts2.actioncontext.ActionContextCompatManager.initialiseActionContextCompat(ActionContextCompatManager.java:36)