Info | ||
---|---|---|
| ||
The intent of this page is to provide a list of issue/resolutions as encountered while migrating from This guide describes how to migrate an existing Struts 2.0.x application to Struts 2.1.x. The ultimate aim It is to have a proper migration guide, but as 2.1.x is still evolving, any of the issue/resolutions found in this page may become obsolete any time. |
Migration issue/resolutions
intended to be read from top to bottom but you may skip-ahead to known problems and common exceptions. Please edit this page or provide comments if you encounter additional issues. |
Table of Contents
Table of Contents | ||||||
---|---|---|---|---|---|---|
|
Update Dependencies
Update your project dependences to use struts2-core-2.1.x and the plugins struts2-core-2.1.x.
Info |
---|
There have been significant changes to the Configuration API between 2.0 and 2.1. Third-party plugins for 2.0 may not be compatible with 2.1.x. |
Maven users can update their project's pom.xml to reference the new core and plugin versions. Ensure no dependencies in the freemarker
groupId are used as the latest version used by Struts is now under the org.freeemarker
groupId and will cause classpath conflicts.
If you need releases not considered General Availability you can use a staging repository where they are usually available:
Code Block |
---|
<repositories>
<repository>
<id>struts-staging</id>
<name>Struts 2.1.6 staging repository</name>
<layout>default</layout>
<url>http://people.apache.org/builds/struts/2.1.6/m2-staging-repository/</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
|
Non-Maven users can can download the jars and dependencies in a Distribution 2.1.6
Note |
---|
DELETE OLD VERSIONS NOW: It's essential that old versions of the jars are removed from your project as well as the deployment directories. Don't trust your IDE to delete unused versions. |
Update Custom Type Converters
If your project implements custom type converters you may need to change the imports statements to use the com.opensymphony.xwork2.conversion package. Failing to do so will cause a compile-time error.
Before:
Code Block |
---|
import com.opensymphony.xwork2.util.TypeConversionException;
|
After:
Code Block |
---|
import com.opensymphony.xwork2.conversion.TypeConversionException;
|
Note: this may also affect conversion classes listed in the xwork-conversion.properties file.
Update struts.xml Configuration
Results and Interceptors have been renamed to use camelCase instead of hyphenated names (eg. now redirectAction instead of redirect-action).
Review all custom interceptor stacks, interceptor refs and results in struts.xml and remove the hyphen.
Interceptors:
From | To |
---|---|
external-ref | externalRef |
model-driven | modelDriven |
static-params | staticParams |
scoped-model-driven | scopedModelDriven |
servlet-config | servletConfig |
token-session | tokenSession |
Results:
From | To |
---|---|
redirect-action | redirectAction |
plaintext | plainText |
Note | ||
---|---|---|
Forgetting to rename an interceptor or result reference will prevent your WebApp from starting. The following is a typical stacktrace for an invalid result type.
|
Migrate plugin configuration
Tiles 2
If you use the Tiles 2 plugin, check your tiles.xml file(s) to ensure they contain a DOCTYPE.
Code Block |
---|
<!DOCTYPE tiles-definitions PUBLIC "-//Apache Software Foundation//DTD Tiles Configuration 2.0//EN"
"http://tiles.apache.org/dtds/tiles-config_2_0.dtd">
<tiles-definitions>
...
|
Note | ||
---|---|---|
A missing DOCTYPE in your Tiles configuration will prevent your WebApp from starting. The following is a typical stacktrace for a missing Tiles DOCTYPE.
|
Ajax Theme / Dojo
If your WebApp uses the ajax theme bundled with Struts 2.0 (ajax tags), you need to update every pageas the Dojo support has been moved into a separate plugin and tag library.
1. Add the Dojo plugin as a new dependency for your project
Code Block |
---|
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-dojo-plugin</artifactId>
<version>2.1.1</version>
</dependency>
|
2. Modify ALL pages that use the ajax theme to use the Dojo tag library.
For each page there are three tasks to complete:
- reference the new Dojo tag library
- update the head tag
- update all ajax themed tags
- check inline scripts
- check inline widgets
Info If this is a major undertaking for your application, it's recommended to modify, test and validate each page one at a time.
Reference the new Dojo Tag Library
Reference the new taglib in your JSP or FTL pages.
Before
Code Block | ||||
---|---|---|---|---|
| ||||
<%@ page contentType="text/html; charset=UTF-8" %>
<%@ taglib prefix="s" uri="/struts-tags"%>
|
After
Code Block | ||||
---|---|---|---|---|
| ||||
<%@ page contentType="text/html; charset=UTF-8" %>
<%@ taglib prefix="s" uri="/struts-tags"%>
<%@ taglib prefix="sx" uri="/struts-dojo-tags" %>
|
Note |
---|
Many pages will require both the core and dojo tags. The sx: prefix is preferred for the Dojo tags. |
Update the head tag
Review the new attributes of the new head tag. You need to decide which values for the cache, compressed, parseContent and extraLocales attributes are appropriate for your application. These settings have major performance implications.
Before:
Code Block | ||||
---|---|---|---|---|
| ||||
<s:head theme="ajax"/>
|
After:
Code Block | ||||
---|---|---|---|---|
| ||||
<sx:head parseContent="true"/>
|
Update all ajax themed tags
Some tags are available only in the Dojo plugin taglib. It will be immediately obvious from your IDE that these tags need to change to the sx: prefix.
Some tags are available in both the core and Dojo plugin taglibs. Any tag that uses the ajax theme needs to be changed to the sx: prefix.
Before:
Code Block | ||||
---|---|---|---|---|
| ||||
<s:url id="jobStatus" includeParams="get" value="/RefreshOptimizationJobStatus.action" />
<s:div id="jobStatus" theme="ajax" href="%{jobStatus}" updateFreq="5000" indicator="indicator">
</s:div>
<img id="indicator" src="img/indicator.gif" alt="Loading..." style="display:none"/>
|
After:
Code Block | ||||
---|---|---|---|---|
| ||||
<s:url var="jobStatus" includeParams="get" value="/RefreshOptimizationJobStatus.action" />
<sx:div id="jobStatus" href="%{#jobStatus}" updateFreq="5000" autoStart="true" indicator="indicator">
</sx:div>
<img id="indicator" src="img/indicator.gif" alt="Loading..." style="display:none"/>
|
Note | |||||||
---|---|---|---|---|---|---|---|
If you forget to migrate a tag reference from the core to the dojo plugin you will receive an exception similar to the one below:
|
Check inline javascript
If your ajax html results contain inline javascript that needs to be executed after updating the DOM, ensure your ajax tags set executeScripts="true" and separateScripts="true". These attributes instruct the widget to search for javascript in the result,
extract it, update the DOM with the result and then execute the javascript.
eg. The following bind will execute scripts within the result when the Submit button is pressed
Code Block |
---|
<s:submit value="Submit" id="submit3" />
<sx:bind targets="div1" highlightColor="#ffffcc" highlightDuration="500" sources="submit3"
events="onclick" href="%{#fragment3Url}" errorNotifyTopics="/error" executeScripts="true"
separateScripts="true"/>
|
Check inline widgets
If your ajax html results include dojo widgets, ensure the head tag's parseContent attribute is true. This instructs dojo to parse the ajax responses for widgets. Note that Struts2.0 always enabled this feature and it could not be turned off. Struts2.1 disables this feature and allows it to be turned on as there is a significant performance impact.
Convert EL expressions to OGNL
Struts2.1 tags do not allow evaluation of JSP EL within their attributes. Instead, Struts2 tags evaluate attribute values as OGNL. Allowing both expression languages within the same attribute opens major security vulnerabilities.
todo: how to convert
Note | ||
---|---|---|
Forgetting to convert attributes to OGNL expressions will produce exceptions similar to the one below:
|
Refactor usage of static methods via OGNL
The default behaviour of allowing access to static methods (via an expression like "@org.somewhere.FooUtls@fooMethod()") has been disabled for security reasons in the 2.1 branch. See https://issues.apache.org/struts/browse/WW-2160 for details. You should either remove these cases, or re-enable the use of static methods explicitly in your configuration by using the struts.ognl.allowStaticMethodAccess property.
Reduce verbose logging
Struts 2.1 introduces more verbose logging than Struts 2.0. While extremely valuable, some users may find these annoying.
Missing Properties
This message states that the framework searched for a property in the value stack and failed to find it.
Code Block | ||||
---|---|---|---|---|
| ||||
2008-04-19 14:21:08,177 WARN [http-8443-Processor25] [OgnlValueStack] Could not find property [templateDir]
2008-04-19 14:21:08,177 WARN [http-8443-Processor25] [OgnlValueStack] Could not find property [templateDir]
2008-04-19 14:21:08,177 WARN [http-8443-Processor25] [OgnlValueStack] Could not find property [templateDir]
2008-04-19 14:21:08,178 WARN [http-8443-Processor25] [OgnlValueStack] Could not find property [org.apache.catalina.jsp_file]
|
To hide these messages, turn off the WARN level logging for OgnlValueStack.
eg. Include a new limit category in your log4j.xml file (only log errors or worse):
Code Block | ||||
---|---|---|---|---|
| ||||
<category name="com.opensymphony.xwork2.ognl.OgnlValueStack">
<priority value="error"/>
</category>
|
TextProvider missing keys
These messages state that the framework searched for text in a resource bundle and failed to find it.
Code Block | ||||
---|---|---|---|---|
| ||||
2008-04-19 14:32:30,106 WARN [http-8443-Processor23] [TextProviderHelper] The first TextProvider in the ValueStack
(package.Action) could not locate the message resource with key 'companyDetails.addressId'
2008-04-19 14:32:30,107 WARN [http-8443-Processor23] [TextProviderHelper] The default value expression
'companyDetails.addressId' evaluated to '10'
|
To hide those messages, turn off the WARN level logging for TextProviderHelper.
eg. Include a new limit category in your log4j.xml file (only log errors or worse):
Code Block | ||||
---|---|---|---|---|
| ||||
<category name="org.apache.struts2.util.TextProviderHelper">
<priority value="error"/>
</category>
|
Redirect and RedirectAction parameters
A (incorrect) change in 2.1.6 has ObjectFactory reporting ERROR's when you attempt to set parameters on a Redirect result.
So a redirect result that sets a parameter like this:
Code Block |
---|
<result type="redirectAction">
<param name="actionName">MyAction</param>
<param name="xxx">${xxx}</param>
</result>
|
Will always give you an error like this:
Code Block |
---|
2009-03-04 16:44:54,201 ERROR com.opensymphony.xwork2.ObjectFactory:27 - Unable to set parameter [xxx] in result of type [org.apache.struts2.dispatcher.ServletActionRedirectResult]
Caught OgnlException while setting property 'xxx' on type 'org.apache.struts2.dispatcher.ServletActionRedirectResult'. - Class: ognl.ObjectPropertyAccessor
File: ObjectPropertyAccessor.java
Method: setProperty
Line: 132 - ognl/ObjectPropertyAccessor.java:132:-1
at com.opensymphony.xwork2.ognl.OgnlUtil.internalSetProperty(OgnlUtil.java:392)
at com.opensymphony.xwork2.ognl.OgnlUtil.setProperty(OgnlUtil.java:143)
at com.opensymphony.xwork2.ognl.OgnlReflectionProvider.setProperty(OgnlReflectionProvider.java:91)
at com.opensymphony.xwork2.ObjectFactory.buildResult(ObjectFactory.java:221)
at com.opensymphony.xwork2.DefaultActionInvocation.createResult(DefaultActionInvocation.java:208)
at com.opensymphony.xwork2.DefaultActionInvocation.executeResult(DefaultActionInvocation.java:355)
|
You can safely ignore and suppress the error by setting the logging level to 'fatal' like this:
Code Block | ||||
---|---|---|---|---|
| ||||
<category name="com.opensymphony.xwork2.ObjectFactory">
<priority value="fatal"/>
</category>
|
Update Unit Tests
There are two known major migration issues affecting user's unit tests.
- The Configuration API now uses a Builder pattern
- The ActionContext.getContext() method does not create a context on demand.
Review the Unit Tests included with Struts2 for recommended practices to setup the Configuration and context.
A quick fix for ActionContext.getContext() returning null
:
Code Block | ||||
---|---|---|---|---|
| ||||
ConfigurationManager configurationManager = new ConfigurationManager();
configurationManager.addContainerProvider(new XWorkConfigurationProvider());
Configuration config = configurationManager.getConfiguration();
Container container = config.getContainer();
ValueStack stack = container.getInstance(ValueStackFactory.class).createValueStack();
stack.getContext().put(ActionContext.CONTAINER, container);
ActionContext.setContext(new ActionContext(stack.getContext()));
assertNotNull(ActionContext.getContext());
|
Explicitly set the includeParams attribute on all url tags to "get" if it is not specified.
The default value of this attribute changed from "get" to "none". See https://issues.apache.org/struts/browse/WW-2901
Filter Mapping, default Action extensions, and Servlets
The default action extension list (struts.action.extension) has changed from just 'action' to 'action' plus "" (no extension). If your application has servlets or other requests that have no extension then they will be mistaken as actions and you will get a "There is no Action mapped for ..." exception like below.
Code Block |
---|
There is no Action mapped for namespace / and action name xxxx. - [unknown location]
at com.opensymphony.xwork2.DefaultActionProxy.prepare(DefaultActionProxy.java:177)
at org.apache.struts2.impl.StrutsActionProxy.prepare(StrutsActionProxy.java:61)
at org.apache.struts2.impl.StrutsActionProxyFactory.createActionProxy(StrutsActionProxyFactory.java:39)
at com.opensymphony.xwork2.DefaultActionProxyFactory.createActionProxy(DefaultActionProxyFactory.java:47)
at org.apache.struts2.dispatcher.Dispatcher.serviceAction(Dispatcher.java:458)
at org.apache.struts2.dispatcher.FilterDispatcher.doFilter(FilterDispatcher.java:395)
|
To fix this problem you can explicitly set the default action extension back to just "action" or you can change your filter mapping for the struts filter.
To set the action extension list back to just "action" add this to your struts.xml file:
Code Block |
---|
<constant name="struts.action.extension" value="action" />
|
If all your actions use the ".action" suffix then you may want to change the Struts filter-mapping so that only actions are handled by the struts filter. Also the FilterDispatcher has been deprecated and replaced by StrutsPrepareAndExecuteFilter.
The web.xml before (matching all requests):
Code Block |
---|
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
|
Change to use the new StrutsPrepareAndExecuteFilter and match just struts requests.
The web.xml after:
Code Block |
---|
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>*.action</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/struts/*</url-pattern>
</filter-mapping>
|
Trouble-shooting
The issues are listed in the same order as encountered after changing jars over from 2.0.x to 2.01.1x. Noteworthy, the migration was done under the following setup: Fedora core 6, JDK 1.6.0_2 and Tomcat 6.0.10 running from MyEclipse plugin.
Result type redirect-action is no longer supported, use redirect instead:
...
Result type "redirect-action" was renamed to "redirectAction":
Code Block |
---|
SEVERE: Exception starting filter struts2 Unable to load configuration. - action - file:/home/giaz/code/.metadata/.plugins/ com.genuitec.eclipse.easie.tomcat.myeclipse/tomcat/webapps/webui/WEB-INF/classes/struts.xml:39:98 at com.opensymphony.xwork2.config.ConfigurationManager.getConfiguration(ConfigurationManager.java:58) at org.apache.struts2.dispatcher.Dispatcher.init_PreloadConfiguration(Dispatcher.java:370) ... Caused by: Error building results for action ScheduleJob in namespace - action - file:/home/giaz/code/.metadata/.plugins/ com.genuitec.eclipse.easie.tomcat.myeclipse/tomcat/webapps/webui/WEB-INF/classes/struts.xml:39:98 at com.opensymphony.xwork2.config.providers.XmlConfigurationProvider.addAction(XmlConfigurationProvider.java:372) ... 30 more Caused by: There is no result type defined for type 'redirect-action' mapped with name 'success' - result - with name 'success' - result - file:/home/giaz/code/.metadata/.plugins/com.genuitec.eclipse.easie.tomcat.myeclipse/tomcat/webapps/webui/ WEB-INF/classes/struts.xml:40:50 at com.opensymphony.xwork2.config.providers.XmlConfigurationProvider.buildResults(XmlConfigurationProvider.java:616) ... 35 more at com.opensymphony.xwork2.config.providers.XmlConfigurationProvider.buildResults(XmlConfigurationProvider.java:616) ... 35 more |
The struts.xml before:
Code Block |
---|
<action name="ScheduleJob" class="com.sag.optimizer.ui.web.action.scheduler.ScheduleJobAction">
<result name="success" type="redirect-action">
<param name="actionName">ListDisplayOptimizationJobStatus</param>
</result>
<result name="error" type="tiles">webui.requestFailed</result>
</action>
|
To resolve this issue modify the struts.xml action definition toThe struts.xml before:
Code Block |
---|
<action name="ScheduleJob" class="com.sag.optimizer.ui.web.action.scheduler.ScheduleJobAction"> <result name="success" type="redirect-actionredirectAction"> <param name="actionName">ListDisplayOptimizationJobStatus</param> </result> <result name="error" type="tiles">webui.requestFailed</result> </action> |
To resolve the issue modify the struts.xml action definition or to:
Code Block |
---|
<action name="ScheduleJob" class="com.sag.optimizer.ui.web.action.scheduler.ScheduleJobAction"> <result name="success" type="redirect">ListDisplayOptimizationJobStatus.action</result> <result name="error" type="tiles">webui.requestFailed</result> </action> |
Tiles 2.1.x plugin tiles.xml now requires DOCTYPE:
Problem Symptom:
Code Block |
---|
Nov 22, 2007 11:38:11 AM org.apache.tiles.impl.BasicTilesContainer init INFO: Initializing Tiles2 container. . . Nov 22, 2007 11:38:11 AM org.apache.commons.digester.Digester error SEVERE: Parse Error at line 2 column 19: Document is invalid: no grammar found. org.xml.sax.SAXParseException: Document is invalid: no grammar found. at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.createSAXParseException(ErrorHandlerWrapper.java:195) at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.error(ErrorHandlerWrapper.java:131) |
Info | ||
---|---|---|
| ||
This is due to the fact that Tiles 2.0.5 now turns validation on as default. |
The tiles.xml page before:
...
Code Block |
---|
<?xml version="1.0" encoding="ISO-8859-1" ?> <!DOCTYPE tiles-definitions PUBLIC "-//Apache Software Foundation//DTD Tiles Configuration 2.0//EN" "http://struts.apache.org/dtds/tiles-config_2_0.dtd"> <tiles-definitions/> |
<s:head theme="ajax"/> is obsolete, use a different theme:
Problem Symptom: Accessing any page that includes <s:head theme="ajax"/> produces the following error:
Code Block |
---|
Nov 22, 2007 1:54:51 PM freemarker.log.JDK14LoggerFactory$JDK14Logger error SEVERE: Expression parameters.parseContent is undefined on line 45, column 28 in template/ajax/head.ftl. The problematic instruction: ---------- ==> ${parameters.parseContent?string} [on line 45, column 26 in template/ajax/head.ftl] ---------- Java backtrace for programmers: ---------- freemarker.core.InvalidReferenceException: Expression parameters.parseContent is undefined on line 45, column 28 in template/ajax/head.ftl. at freemarker.core.TemplateObject.assertNonNull(TemplateObject.java:124) at freemarker.core.TemplateObject.invalidTypeException(TemplateObject.java:134) |
Resolution is to either remove the "<s:head theme="ajax"/>" or use Use a different theme e.g. <s:head theme="xhtml" />
...
Ajax UI tags were moved to the new dojo plugin, use /struts-dojo-tags taglib instead of (or in
...
addition to) /struts-tags:
Problem Symptom: Accessing a page containing ajax UI tags through using the struts 2 taglib will produce the following error:
...
Code Block |
---|
<%@ page contentType="text/html; charset=UTF-8" language="java" %> <%@ taglib prefix="s" uri="/struts-tags"%> <%@ taglib prefix="sx" uri="/struts-dojo-tags" %> <s:url var="jobStatus" includeParams="get" value="/RefreshOptimizationJobStatus.action" /> <sx:div id="jobStatus" href="%{#jobStatus}" updateFreq="5000" autoStart="true" indicator="indicator"> </sx:div> <img id="indicator" src="img/indicator.gif" alt="Loading..." style="display:none"/> |
Info | ||
---|---|---|
| ||
Note the use of remote div is now through the dojo plugin taglib sx. Other ui tags are also no longer available through the /struts-tags taglib but only through the /struts-dojo-tags taglib: datetimepicker and autocompleter. |
This is not really a migration issue but rather seems a bug when migrating:
Problem Symptom: You receive the following exception accessing a page:
Code Block |
---|
WARNING: Could not find property [struts.valueStack]
ognl.OgnlException: target is null for setProperty(null, "preventCache", [Ljava.lang.String;@5f262a85)
at ognl.OgnlRuntime.setProperty(OgnlRuntime.java:1651)
at ognl.ASTProperty.setValueBody(ASTProperty.java:101)
at ognl.SimpleNode.evaluateSetValueBody(SimpleNode.java:177)
at ognl.SimpleNode.setValue(SimpleNode.java:246)
at ognl.ASTChain.setValueBody(ASTChain.java:172)
|
Resolution is to change set devMode to false in struts.xml e.g.
from
Code Block |
---|
<constant name="struts.devMode" value="true" />
|
to
...
User-defined converter (subclassing StrutsTypeConverter) will no longer be needed when using datetimepicker:
Problem Symptom: Your custom StrutsTypeConverter implementation does not longer work in version 2.1.x. In 2.0.x you needed to implement a custom StrutsTypeConverter e.g. StringToDateConverter to be able to parse and convert to Date the String posted from a datetimepicker control into the action. In version 2.0.x datetimepicker was posting a String formatted as specified in the "displayFormat" field e.g.
Code Block |
---|
<%@ taglib prefix="s" uri="/struts-tags"%>
<s:datetimepicker label="Begin Date" name="beginDate" displayFormat="yyyy.MM.dd">
|
In version 2.1.x datetimepicker will post a String Date in RFC 3339 format, so you can define your setter to receive a Date directly and avoid using converters for this purpose e.g.
Code Block |
---|
<%@ taglib prefix="s" uri="/struts-tags"%>
<%@ taglib prefix="sx" uri="/struts-dojo-tags" %>
<sx:datetimepicker label="Begin Date" name="beginDate" displayFormat="yyyy.MM.dd">
|
ActionMappingParams
Parameters set by the action mapping are not set/not available through ParameterAware (This change is only needed when going to 2.1.x (where x>0))
These are now only available if you use the new interceptor named "actionMappingParams".
...