PSA - Confluence moves to Struts in 8.0 - EAP


We have found another issue related to xwork actions configuration in the descriptor file.
For intercepting confluence view page action we configured in the descriptor our custom view page action in the following way

<xwork name="Custom Actions" key="customactions">
	<package name="pages" extends="pages" namespace="/pages">
	            <default-interceptor-ref name="validatingStack"/>
	            <action name="viewpage" class="xxx.ViewPageAction">

This configuration does not work in Confluence 8 and it does not take into account the interceptor custom action (confluence bypasses it). In logs we can see the following error when we install the app (the app is enabled but does not work as expected):

java.lang.RuntimeException: The following packages participate in cycles: pages. Listener: com.atlassian.confluence.impl.struts.StrutsStateChangeListener event:
Caused by: The following packages participate in cycles: pages - [unknown location]

If we change the name of the package name for Confluence 8 it starts again working but not in Confluence 7 (tested in 7.13.11)

Any way to configure it being compatible with both Confluence versions?



1 Like

@hugo @GorkaGalloBustinza
We have released version 1.5.4 of confluence-compat-lib which rectifies the issues you were seeing in ActionContextCompatManager.


Interesting, we see something similar but different :slight_smile:
when we build our app with target Confluence version 8+ and attempt to deploy it on Confluence 7 we see

NoClassDefFoundError: com/opensymphony/xwork/ActionSupport

Building the app against Confluence 7 and deploying it on Confluence 8 seems to work in both versions

And thanks a lot to @jens and @Kusal for bits of wisdom around calls to “addActionMessage” methods. Helped a lot to solve the original problem

1 Like

One trick for tracking down unexpected dependencies is to look at the generated bytecode. I’m not sure if this will help for transitive dependencies, but it can certainly surface useful information:

javap -c -p -s -v -sysinfo -constants ./path/to/MyClassName.class

Hi @Kusal ,

Thanks for the fix, apparently we do not have these issues anymore. But checking the logs we have realised that during the reindex of any page in Confluence 8 (tested in 8.0.2) any call to the actionContextCompatManager component prints an error like this:

ERROR [Caesium-1-1] [compat.struts2.actioncontext.ActionContextCompatManager] getContextMap Couldn't get the ContextMap

This is making a lot of noise in the server logs. In Confluence 7.13 we do not have this behaviour as it is retrieving an object

Is this different behaviour expected between both versions of Confluence?
Could you check if this behaviour is correct and if so, change the log level to not make so many noise in the logs?



This is expected behaviour when you attempt to operate on the ActionContext when it does not exist. It works in Confluence 7.x as the previous version of ActionContext would always create an empty one when one did not exist, whereas in Confluence 8.x it is only created when appropriate (ie. a request thread).

There is no expectation that plugins compiled against Confluence 8 will work with older versions of Confluence.

Yes, this is intended for most plugins, especially when compiled against a newer minor version of Confluence 7.

1 Like

@Kusal Now we have tried to compile and build the plugin against Confluence 7 and it is deployable on Confluence 8 (removed all direct dependencies on “xwork” from “com.opensymphony.”)
But we see these errors on “some actions”

Caused by: java.lang.IllegalStateException: Failed to introspect Class [com.vertuna.confluence.plugins.MyAction] from ClassLoader [com.vertuna.confluence.plugins.myapp [315]]
[INFO] [talledLocalContainer] 	at org.springframework.util.ReflectionUtils.getDeclaredMethods(
[INFO] [talledLocalContainer] 	at org.springframework.util.ReflectionUtils.doWithLocalMethods(
[INFO] [talledLocalContainer] 	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.determineCandidateConstructors(
[INFO] [talledLocalContainer] 	... 353 more
[INFO] [talledLocalContainer] Caused by: java.lang.NoClassDefFoundError: com/opensymphony/xwork/ActionSupport
[INFO] [talledLocalContainer] 	at java.base/java.lang.Class.getDeclaredMethods0(Native Method)
[INFO] [talledLocalContainer] 	at java.base/java.lang.Class.privateGetDeclaredMethods(
[INFO] [talledLocalContainer] 	at java.base/java.lang.Class.getDeclaredMethods(
[INFO] [talledLocalContainer] 	at org.springframework.util.ReflectionUtils.getDeclaredMethods(
[INFO] [talledLocalContainer] 	... 355 more
[INFO] [talledLocalContainer] Caused by: java.lang.ClassNotFoundException: com.opensymphony.xwork.ActionSupport
[INFO] [talledLocalContainer] 	at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(
[INFO] [talledLocalContainer] 	at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(
[INFO] [talledLocalContainer] 	at org.apache.felix.framework.ExtensionManager$ExtensionManagerWiring.getClassByDelegation(

Why this happens?

If we compile and build the same source code for Confluence 8 this starts to work - no exceptions thrown

Could you confirm it is possible to have one build that works for Confluence 7 and Confluence 8 or shall we split and have special versions for each major Confluence version?


You can use a bytecode viewer on the compiled class to find the line of code which is causing a dependency on com.opensymphony.xwork.ActionSupport. Alternatively, you may strip all the code from your action class and slowly reintroduce methods/lines of code until the exception appears.

1 Like