Struts 2 > Upgrading from 1.4 |
Webwork1.x was seperated into two projects, XWork and Webwork. From this, several classes have been moved to different package names.
java org.apache.xalan.xslt.Process -IN actions.xml -XSL actions.xsl -OUT xwork.xml
Remember that you'll need to Xalan libraries in your classpath to run the above command.
If you want to look at these pages directly in your browser, I recommend user Internet Explorer as it automagically formats XML documents reasonably. There one caveat though. WW1 had a way to shorten the declaration of actions by allowing you to specify a package prefix in webwork.properties file. Since this information is outside the actions.xml file, the XSLT is unable to take advantage of it. Consequently, you might need to edit the xwork.xml file to update the class names.
WebWork 1.x configuration used a pull paradigm to load action configurations when they are asked for, whereas WebWork2 builds the configuration up-front to make the configuration queryable. The webwork.MigrationConfiguration must therefore act as an adapter between these two paradigms. It does this by returning a custom RuntimeConfiguration which first tries the default XWork Configuration (which, by default, loads configuration information from a file named "xwork.xml" in the root of the classpath) and then attempts to load action configuration using the Configuration classes from WebWork 1.x. In this way, an application can be slowly converted over to WebWork2 while reusing the configuration and Actions from a WebWork 1.x application. One caveat in this is that your migrated application MUST be rebuilt against the WebWork2 and migration jar files, as the classloader will rightly recognize that the webwork.Action and webwork.ActionSupport in WebWork 1.x are not the same as those provided by the migration jar files. Other than that, it should be seamless (and let us know if it isn't).
If the webwork.MigrationRuntimeConfiguration does not find the action configuration using the RuntimeConfiguration from the supplied RuntimeConfiguration (which is acquried from the Xwork DefaultConfiguration and will load configurations from all of the sources configured for XWork / WebWork2), it will build an ActionConfiguration by instantiating an Action using the ActionFactories from WebWork 1.x. The ActionFactory stack used is a subset of the default ActionFactory stack used in WebWork 1.x:
factory = new JavaActionFactory(); factory = new ScriptActionFactoryProxy(factory); factory = new XMLActionFactoryProxy(factory); factory = new PrefixActionFactoryProxy(factory); factory = new JspActionFactoryProxy(factory); factory = new CommandActionFactoryProxy(factory); factory = new AliasingActionFactoryProxy(factory); factory = new CommandActionFactoryProxy(factory); factory = new ContextActionFactoryProxy(factory);
Some of the ActionFactory classes have been left out as they are handled by Interceptors in WebWork2. If the Action instance is created (meaning that the configuration has been found in the webwork.properties or actions.xml files used by the WebWork 1.x configuration classes) a parameter Map is created by introspecting the Action instance. A Map is needed for results and, again, WebWork 1.x used a pull paradigm to find results when they were needed, so a LazyResultMap is created which extends HashMap and overrides get() to look up the Result configuration if it has not previously been loaded. If the result ends in the Action suffix (defaulting to ".action"), then a ChainingResult is created, otherwise a ServletDispatcherResult is created. Using the Action class of the instantiated Action, the Map of parameters introspected from the Action instance, and the LazyResultMap, a new ActionConfig is created. The ActionConfig is saved into a special Package, "webwork-migration", so that it will pick up the default Interceptor stack defined for that package. The "webwork-migration" package is defined in a webwork-migration.xml file which is included in the migration jar file and which is automatically added to the Xwork configuration providers when the MigrationConfiguration is used:
<!DOCTYPE xwork PUBLIC "-//OpenSymphony Group//XWork 1.0//EN" "http://www.opensymphony.com/xwork/xwork-1.0.dtd"> <xwork> <include file="webwork-default.xml"/> <package name="webwork-migration" abstract="true" extends="webwork-default"> <interceptors> <interceptor-stack name="migrationStack"> <interceptor-ref name="timer"/> <interceptor-ref name="logger"/> <interceptor-ref name="chain"/> <interceptor-ref name="static-params"/> <interceptor-ref name="prepare"/> <interceptor-ref name="params"/> <interceptor-ref name="workflow"/> </interceptor-stack> </interceptors> <default-interceptor-ref name="migrationStack"/> </package> </xwork>
Here we can see that a number of the functions previously performed by ActionFactories in WebWork 1.x are now replaced by Interceptors in WebWork2, including the parameters, the chaining, calling prepare(), and the workflow (which was formerly implemented in ActionSupport in WebWork 1.x).
By creating and saving the ActionConfig in the PackageConfig for the "webwork-migration" package, the ActionConfig for the migrated Action is available for future calls, obviating the need to re-parse the old configuration files and making the configuration for the Action available for querying via the configuration API for tools such as the configuration browser.
The biggest change is the use of OGNL for accessing object properties. Properties are no longer accessed with a forward slash "/" but with a dot "." Also, rather than using ".." to traverse down the stack, we now use "[n]" where n is some positive number. Lastly, in WebWork 1.x one could access special named objects (the request scope attributes to be exact) by using "@foo", but now special variables are accessed using "#foo". However, it is important to note that "#foo" does NOT access the request attributes. "#foo" is merely a request to another object in the OgnlContext other than the root. See OGNL reference for more details.
Also see JSP Expression Language Comparison with WebWork 1.x for a table of the expression language changes.
The property tag is now only used to print out values from the stack. In WW1, it was also used to set a variable in the scope, and to push properties to the top of the stack. These functions are now performed by the set and push tags.
The action tag does not evaluate the body section any more and does not push the executed action onto the ValueStack. Instead, use the "id" attribute to assign a name to the action and reference it as "#id".
Lets enumerate some examples of differences between code snips using Home and Home.
There are numerous changes in syntax. First of all there are new tags and secondly there is a new expression language. Here's a small example:
Webwork 1
<ww:property value="a/b"> <ww:property value="foo" /> </ww:property>
Webwork 2
<ww:push value="a.b"> <ww:property value="foo" /> </ww:push>
One can note that the "push" tag doesn't just push it pops too at the end of the tag. Surprise! Also note the "." instead of the "/" for traversing object properties.
<webwork:if test="hasErrorMessages == true"> ERROR:<br /> <font color="red"> <webwork:iterator value="errorMessages"> <webwork:property/><br /> </webwork:iterator> </font> </webwork:if>
Webwork 2
<webwork:if test="hasErrors()"> ERROR:<br /> <font color="red"> <webwork:iterator value="actionErrors"> <webwork:property/><br /> </webwork:iterator> </font> </webwork:if>
<servlet> <servlet-name>velocity</servlet-name> <servlet-class>com.opensymphony.webwork.views.velocity.WebWorkVelocityServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet>
webwork.velocity.configfile=velocity.properties
WebWork will use this file to initialize the Velocity engine. The search path for the file is:
It might be possible to copy WW1's ResultException, and write an Interceptor that catches the ResultExceptions and add the result of getMessage() to the actionErrors of the
executed Action and return ResultException.getResult().
Maybe it would be possible to include ResultException in WW2 too to make migration easier?!
It can be replaced by directly using java.text.DateFormat
The new method to use is addFieldError(String, String).
The new method is now addActionError(String).
The ValueStack is com.opensymphony.xwork.util.OgnlValueStack
The old methods pushValue and popValue are renamed to simply push and
pop.
An instance of the ValueStack can be obtained by using ActionContext.getContext().getValueStack instead of the old ValueStack.getStack().
Instead of implementing ServletRequestAware etc the
[Servlet]ActionContext.getXXX-methods can be used to obtain application-map, request, response etc.
The CommandDriven interface is removed. It is not neccesary to implement a special interface when working with commands anymore. Use the method attribute in your action-Element in xwork.xml to tell xwork which method to invoke on your action.
You can see which alias you're accessing by doing this: ActionContext.getContext().getActionInvocation().getProxy().getActionName()