Struts 2 upgrade
Confluence is working to upgrade from WebWork 2.1.x to Apache Struts 2 in Confluence 8.0.
We've been on WebWork 2.1.x for a long time while maintaining its open-source library. To modernise our code base, reduce tech debt and secure against OGNL attacks, we've decided to move to Struts 2.
This page contains a migration plan, breaking changes as well as some future improvements you can expect. We'll continue to share updates on this page as we progress with the Struts 2 upgrade.
- Breaking changes
- 3 July 2023 update (new version of compat-lib 1.6.0)
- 19 January 2023 update (new version of compat-lib 1.5.4)
- 21 November 2022 update (new version of compat-lib 1.5.3)
- 9 November 2022 update (new version of compat-lib 1.5.1)
- 11 October 2022 update (8.0.0-m69)
- 30 September 2022 update (more details on compat-lib 1.5.0)
- 21 September 2022 update (8.0.0-struts-m48)
- 9 September 2022 mupdate (8.0.0-struts-m39)
- 30 August 2022 update (8.0.0-struts-m027)
- 2 August 2022 update (8.0.0-struts-m020)
How to test
You can test your plugin against our Struts development release and by removing XWork/WebWork in your dependencies. If your plugin uses confluence-plugins-platform-pom
then it should get Struts 2 as managed dependency now.
Background on WebWork/Struts relationship
The WebWork framework spun off from Apache Struts 1 aiming to offer enhancements and refinements while retaining the same general architecture of the original Struts framework. In December 2005, it was announced that WebWork 2.2 was adopted as Apache Struts 2, which reached its first full release in February 2007.[1]
Overview of breaking changes
If your plugin has classes extending
ActionSupport
directly, we'd recommend extendingConfluenceActionSupport
to get some compatibility support from Confluence, or if not possible, then move tocom.opensymphony.xwork2.ActionSupport
$webwork.htmlEncode
will no longer be available in the Velocity context,$htmlUtil.htmlEncode
will be used instead.The AroundInterceptor has been removed. If your application extends the AroundInterceptor, you can either import the class into your source code from WebWork 2 (pursuant to the Open Symphony License) and modify it to server as your own base class, or rewrite your interceptor.
There are a few new package coordinates and class replacements that are breaking in nature listed in the table below.
Webwork2.x | Struts2 | Comments |
---|---|---|
com.opensymphony.xwork.* | com.opensymphony.xwork2.* | |
com.opensymphony.webwork.* | org.apache.struts2.* | |
DispatcherUtil | Dispatcher | |
com.opensymphony.webwork.config.Configuration | org.apache.struts2.config.Settings | |
com.opensymphony.webwork.ServletActionContext | org.apache.struts2.ServletActionContext | Compatibility layer class: com.atlassian.confluence.compat.struts2.servletactioncontext.ServletActionContextCompatManager |
$req | $request | For velocity VM files only. For now, we have put $req for backward compatibility, we might clean this up by end of 8.0 |
$res | $response | For velocity VM files only. For now, we have put $res for backward compatibility, we might clean this up by end of 8.0 |
#bodytag( "Component" <parameters>) | #scomponent(<parameters>) #end #stextfield(<parameters>) | Moved away from #tag and #bodytag directives to #s based new form of component directives. So all these directive tags have now been moved to Struts format. For details and examples, see 30 August update. |
//Old format
|
//New format does have the first letter of the property name in uppercase
| If your Struts action has a setter method for a property that starts with a lowercase letter followed by an uppercase letter, for example pRoperty , make sure it follows the new format set<pRoperty> |
The convention of trying a "do" form of an action method is supported in WebWork.
In WebWork:
| The convention of trying a "do" form of an action method is NOT supported in Struts 2.
In Struts:
| The convention of trying a "do" form of an action method is not supported in Struts 2. The convention of calling the "default" method via "doDefault" is not supported in Struts 2. |
Update from
New version of compat-lib
We have released a new version of compat-lib (1.6.0) that contains the following changes:
- Fixed
getParameters
method not being consistent between WebWork and Struts2 - Auto-initialisation of ActionContext when using #setContextMap
- Enforced type safety where necessary
- Implemented Struts 6.3 support
This updated version requires the following additional package import:
com.opensymphony.xwork2.conversion.impl;resolution:=optional,
Update from
New version of compat-lib
We have released a new version of compat-lib (1.5.4) that contains the following fix:
ActionContextCompatManager was caching the ActionContext instance upon first method call, resulting in this cached instance being returned for new requests/threads. This is now rectified.
CONFSERVER-81828 - Getting issue details... STATUS
Update from
New version of compat-lib
We have released a new version of compat-lib (1.5.3) that contains the following change:
ServletActionContextCompatManager#getRequest will now return null when ServletActionContext#getRequest would return null or throw an NPE. For developers that desire the previous behaviour, please use StaticHttpContext#getRequest instead.
Update from
New version of compat-lib
We have released a new version of compat-lib (1.5.1) that contains the following changes:
- A fix for
ActionContextCompatManager.setParameters
method. In Confluence 8.0 installed plugins, we are returningMap<String String[]>
instead ofHttpParameters
to provide backward compatibility. - A new method
getServletContext
inServletActionContextCompatManager.
This updated version requires the following additional package import when compared to the previously published documentation.
org.apache.struts2.dispatcher;resolution:=optional,
Update from
8.0.0-m69
Confluence 8.0.0-m69 and newer EAPs are based on Struts 2.5.x. That is, all work for the Struts 2 feature has been merged to master.
The following are known issues with this feature:
- When the text-encoding setting is changed from Confluence General Configuration (/admin/viewgeneralconfig.action), Struts does not reflect this change immediately. It will only change following a server restart, or plugin install/uninstall. See CONFSERVER-80123 - Getting issue details... STATUS for more information.
- Plugins with XWork modules may malfunction or fail to enable or uninstall in certain scenarios. See CONFSERVER-80124 - Getting issue details... STATUS for more information.
Update from
More details on compat-lib 1.5.0
As we mentioned in our last update, we were developing a compat-lib to handle plugin cross-compatibility for XWork/WebWork and Struts across Confluence 7.x and 8.x. We have now released 1.5.0 version of confluence-compat-lib
.
We have identified two classes that are used the most, and provided the following compatibility layers for them.
We have listed the compatibility layer in the third column of this table. Scroll the table to see the compatibility layer class.
XWork/Webwork class | Struts 2 class | Compatibility layer class |
---|---|---|
com.opensymphony.webwork.ServletActionContext | org.apache.struts2.ServletActionContext | com.atlassian.confluence.compat.struts2.servletactioncontext.ServletActionContextCompatManager |
com.opensymphony.xwork.ActionContext | com.opensymphony.xwork2.ActionContext | com.atlassian.confluence.compat.struts2.actioncontext.ActionContextCompatManager |
How to keep a single version of a plugin compatible
If you only want to keep a single version of a plugin compatible across Confluence 7.x and 8.x, you will need to add the following dependency:
<dependency>
<groupId>com.atlassian.confluence.compat</groupId>
<artifactId>confluence-compat-lib</artifactId>
<version>1.5.1</version>
</dependency>
- For ServletActionContext, use
ServletActionContextCompatManager
- If you only want to use
getRequest()
,getResponse()
orgetContext()
, you can also usecom.atlassian.core.filters.ServletContextThreadLocal
as this class is available on both Confluence 7.x and 8.x already from Confluence core.
- If you only want to use
- For ActionContext, use
ActionContextCompatManager
Use the below example code to replace ServletActionContext to make your plugin compatible across old and new versions of Confluence.
In spring-components.xml (or a Spring Configuration class)
<beans:bean id="servletActionContextCompat" class="com.atlassian.confluence.compat.struts2.servletactioncontext.ServletActionContextCompatManager"/>
// Wiring in a class
public void setServletActionContextCompatManager(@Qualifier("servletActionContextCompat") ServletActionContextCompatManager servletActionContextCompatManager) {
this.servletActionContextCompatManager = servletActionContextCompatManager;
}
// Usage in a class
public HttpServletRequest useActionContextForRequest() {
return this.servletActionContextCompatManager.getRequest();
}
Add the following packages to the 'Import-Package' section of amps-maven-plugin
or confluence-maven-plugin
in your pom.xml:
<plugin>
<groupId>com.atlassian.maven.plugins</groupId>
<artifactId>amps-maven-plugin</artifactId>
<configuration>
<instructions>
<Import-Package>
com.opensymphony.xwork;resolution:=optional,
com.opensymphony.webwork;resolution:=optional,
org.apache.struts2;resolution:=optional,
org.apache.struts2.dispatcher;resolution:=optional,
com.opensymphony.xwork2;resolution:=optional
</Import-Package>
</instructions>
</configuration>
</plugin>
This is the current structure of the compat interfaces for replacement usages:
/**
* Provides version-agnostic implementations for ServletActionContext methods.
*/
interface ServletActionContextCompat {
void setRequest(HttpServletRequest request);
HttpServletRequest getRequest();
void setResponse(HttpServletResponse response);
HttpServletResponse getResponse();
void setServletConfig(ServletConfig config);
ServletConfig getServletConfig();
}
/**
* Provides version-agnostic implementations for ActionContext methods.
*/
interface ActionContextCompat {
void setApplication(Map application);
Map getApplication();
void setContextMap(Map contextMap);
Map getContextMap();
void setConversionErrors(Map conversionErrors);
Map getConversionErrors();
void setLocale(Locale locale);
Locale getLocale();
void setName(String name);
String getName();
void setParameters(Map parameters);
Map getParameters();
void setSession(Map session);
Map getSession();
Object get(Object key);
void put(Object key, Object value);
}
Update from
Milestone 8.0.0-struts-m48
- If your Struts action has a setter method for a property that starts with a lowercase letter followed by an uppercase letter, for example
pRoperty
, ensure it follows the new format like:set<pRoperty>
private String xHtml;
//Old format
public void setXHtml(String value) {
....
}
//New format does not have first letter of the property name in uppercase
public void setxHtml(String value) {
....
}
- There is also a change in behaviour to "do" Methods:
doActionMethod | The convention of trying a "do" form of an action method is not supported.
In WebWork:
In Struts2:
|
---|---|
default method | Calling the "default" method via "doDefault" is not supported. |
To learn more, see Key changes from WebWork2 for more details.
- The following table tracks issues fixed in this milestone:
Confluence compat lib 1.5.0 is coming
A compat lib to handle plugin cross-compatibility for the Struts 2 upgrade is currently in development. We are targeting next week for a release. The compat lib will only address the most widely used classes across our plugins.
Update from
Milestone 8.0.0-struts-m39
404s on UPM-based plugin re-install has been fixed by wiring the enabled/disabled lifecycle.
- The following table tracks issues fixed in this milestone:
Update from
Milestone 8.0.0-struts-m027
- Moved away from #tag and #bodytag directives to #s based new form of component directives. So all these directive tags have now been moved to Struts format. We plan to remove support for #tag/#bodytag in the next few days. Two examples that show the changes needed are:
#tag/#bodytag | New #s based tag directive |
---|---|
#bodytag( "Component" "label='create.support.zip.include.tomcat'" "name='includeServerLogs'" "value=includeServerLogs" "theme='aui'" "template='onofflist.vm'")}} #param ("description" "$i18n.getText('create.support.zip.include.tomcat.desc')") #end | #scomponent("label='create.support.zip.include.tomcat'" "name='includeServerLogs'" "value=includeServerLogs" "theme='aui'" "template='onofflist.vm'" "description=$i18n.getText('create.support.zip.include.tomcat.desc')") #end |
#bodytag( "TextField" "label='create.support.zip.logs.other.directory'" "name='serverLogsDirectory'" "value=serverLogsDirectory" "theme='aui'") | #stextfield("label='create.support.zip.logs.other.directory'" "name='serverLogsDirectory'" "value=serverLogsDirectory" "theme='aui'" Note that only |
OGNL static method calls in
atlassian-plugin.xml
action wiring or in a velocity file won't work anymore. You will need to wrap that in the corresponding action in a new method and provide just the String/Object needed by Velocity file.For example:
<result name="success" type="redirectwithflash">${@com.atlassian.confluence.util.GeneralUtil@getPageUrl(page)}</result>
can be replaced with:
<result name="success" type="redirectwithflash">${pageUrl}</result>
where-in a new getPageURL
is exposed from the action itself.
Editor and attachments are now working. Our tests show that we have >98% of Confluence features working with this milestone.
- The following table tracks issues fixed in this milestone:
Update from
Milestone 8.0.0-struts-m021
- Note that we are targeting Struts 2.5.x first, and may bring out the newest Struts version 6.0 (known as 2.6 in old Struts style) at a later date.
- The following table tracks issues of the known broken areas, and we'll try to keep the new incoming ones public. Additionally, there may be some broken areas in Velocity Struts rendering integration not listed in the table.