Struts 2 upgrade

On this page

Still need help?

The Atlassian Community is here for you.

Ask the community

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.

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 extending ConfluenceActionSupport to get some compatibility support from Confluence, or if not possible, then move to com.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.xStruts2Comments
com.opensymphony.xwork.*com.opensymphony.xwork2.*
com.opensymphony.webwork.*org.apache.struts2.*
DispatcherUtilDispatcher
com.opensymphony.webwork.config.Configurationorg.apache.struts2.config.Settings
com.opensymphony.webwork.ServletActionContextorg.apache.struts2.ServletActionContext

Compatibility layer class:

com.atlassian.confluence.compat.struts2.servletactioncontext.ServletActionContextCompatManager

$req$requestFor velocity VM files only. For now, we have put $req for backward compatibility, we might clean this up by end of 8.0
$res$responseFor 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>)
#bodytag( "TextField" <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.

private String xHtml; 

//Old format

public void setXHtml(String value) { .... } 

private String xHtml; 

//New format does have the first letter of the property name in uppercase

public void setxHtml(String value) { .... } 

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.

<action name="..." method="submit">   ... </action> 

In WebWork:

  • step 1: try to execute submit method in the action will fail
  • step 2: try to execute doSubmit method in the action, if Step 1 is fail, will fail


The convention of trying a "do" form of an action method is NOT supported in Struts 2.

<action name="..." method="submit">   ... </action>

In Struts:

  • try to execute submit method in the action will fail

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:

  1. A fix for ActionContextCompatManager.setParameters method. In Confluence 8.0 installed plugins, we are returning Map<String String[]> instead of HttpParameters to provide backward compatibility.
  2. A new method getServletContext in ServletActionContextCompatManager.

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 classStruts 2 classCompatibility layer class
com.opensymphony.webwork.ServletActionContextorg.apache.struts2.ServletActionContextcom.atlassian.confluence.compat.struts2.servletactioncontext.ServletActionContextCompatManager
com.opensymphony.xwork.ActionContextcom.opensymphony.xwork2.ActionContextcom.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()  or getContext(), you can also use com.atlassian.core.filters.ServletContextThreadLocal as this class is available on both Confluence 7.x and 8.x already from Confluence core.
  • For ActionContext, use ActionContextCompatManager 

Use the below example code to replace ServletActionContext to make your plugin compatible across old and new versions of Confluence.

See an example
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:

See an example
<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>
See an example
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.

<action name="..." method="submit"> 2  ... 3</action>

In WebWork:

  • step 1: try to execute submit method in the action will fail

  • step 2: try to execute doSubmit method in the action, if Step 1 has failed, will fail

In Struts2:

  • try to execute submit method in the action will fail

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: 

    Key Summary T Created Updated Due Assignee Reporter P Status Resolution
    Loading...
    Refresh

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:

Key Summary T Created Updated Due Assignee Reporter P Status Resolution
Loading...
Refresh


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'")
#param ("description" "$i18n.getText('create.support.zip.logs.other.directory.desc')")
#end

#stextfield("label='create.support.zip.logs.other.directory'" "name='serverLogsDirectory'" "value=serverLogsDirectory" "theme='aui'"
"description=$i18n.getText('create.support.zip.logs.other.directory.desc')")


Note that only Component type tags require #end closing tag.

  • 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:

Key Summary T Created Updated Due Assignee Reporter P Status Resolution
Loading...
Refresh

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.

Key Summary T Created Updated Due Assignee Reporter P Status Resolution
Loading...
Refresh


Last modified on Oct 10, 2023

Was this helpful?

Yes
No
Provide feedback about this article
Powered by Confluence and Scroll Viewport.