Most use cases can be divided into two phases. First, we need to change or query the application's state, and then we need to present an updated view of the application. The Action class manages the application's state, and the Result Type manages the view.
Predefined Result Types
The framework provides several implementations of the com.opensymphony.xwork2.Result interface, ready to use in your own applications.
Chain Result | Used for Action Chaining |
Dispatcher Result | Used for web resource integration, including JSP integration |
FreeMarker Result | Used for FreeMarker integration |
HttpHeader Result | Used to control special HTTP behaviors |
Redirect Result | Used to redirect to another URL (web resource) |
Redirect Action Result | Used to redirect to another action mapping |
Stream Result | Used to stream an InputStream back to the browser (usually for file downloads) |
Velocity Result | Used for Velocity integration |
XSL Result | Used for XML/XSLT integration |
PlainText Result | Used to display the raw content of a particular page (i.e jsp, HTML) |
Tiles Result | Used to provide Tiles integration |
Optional
JasperReports Plugin | Used for JasperReports Tutorial integration | Optional, third-party plugin |
Additional Result Types can be created and plugged into an application by implementing the com.opensymphony.xwork2.Result interface. Custom Result Types might include generating an email or JMS message, generating images, and so forth.
Default Parameters
To minimize configuration, Results can be configured with a single value, which will be converted into a parameter, and each Result can specify which parameter this value should be set as. For example, here is a result defined in XML that uses a default parameter:
<result type="freemarker">foo.fm</result>
That is the equivalent to this:
<result type="freemarker"> <param name="location">foo.vm</param> </result>
Since probably 95% of your actions won't need results that contain multiple parameters, this little shortcut saves you a significant amount of configuration. It also follows that if you have specified the default parameter, you don't need to set the same parameter as a specifically-named parameter.
Registering Result Types
All Result Types are plugged in via the Result Configuration.
This result invokes an entire other action, complete with it's own interceptor stack and result.
Parameters
actionName (default) - the name of the action that will be chained to
namespace - used to determine which namespace the Action is in that we're chaining. If namespace is null, this defaults to the current namespace
method - used to specify another method on target action to be invoked. If null, this defaults to execute method
skipActions - (optional) the list of comma separated action names for the actions that could be chained to
Examples
<package name="public" extends="struts-default"> <!-- Chain creatAccount to login, using the default parameter --> <action name="createAccount" class="..."> <result type="chain">login</result> </action> <action name="login" class="..."> <!-- Chain to another namespace --> <result type="chain"> <param name="actionName">dashboard</param> <param name="namespace">/secure</param> </result> </action> </package> <package name="secure" extends="struts-default" namespace="/secure"> <action name="dashboard" class="..."> <result>dashboard.jsp</result> </action> </package>
Includes or forwards to a view (usually a jsp). Behind the scenes Struts will use a RequestDispatcher, where the target servlet/JSP receives the same request/response objects as the original servlet/JSP. Therefore, you can pass data between them using request.setAttribute() - the Struts action is available.
There are three possible ways the result can be executed:
If we are in the scope of a JSP (a PageContext is available), PageContext's {@link PageContext#include(String) include} method is called.
If there is no PageContext and we're not in any sort of include (there is no "javax.servlet.include.servlet_path" in the request attributes), then a call to {@link RequestDispatcher#forward(javax.servlet.ServletRequest, javax.servlet.ServletResponse) forward} is made.
Otherwise, {@link RequestDispatcher#include(javax.servlet.ServletRequest, javax.servlet.ServletResponse) include} is called.
Parameters
location (default) - the location to go to after execution (ex. jsp).
parse - true by default. If set to false, the location param will not be parsed for Ognl expressions.
Examples
<result name="success" type="dispatcher"> <param name="location">foo.jsp</param> </result>
Renders a view using the Freemarker template engine.
The FreemarkarManager class configures the template loaders so that the template location can be either
relative to the web root folder. eg
/WEB-INF/views/home.ftl
a classpath resuorce. eg
com/company/web/views/home.ftl
Also see Freemarker Support.
Parameters
location (default) - the location of the template to process.
parse - true by default. If set to false, the location param will not be parsed for Ognl expressions.
contentType - defaults to "text/html" unless specified.
writeIfCompleted - false by default, write to stream only if there isn't any error processing the template. Setting template_exception_handler=rethrow in freemarker.properties will have the same effect.
Examples
<result name="success" type="freemarker">foo.ftl</result>
A custom Result type for setting HTTP headers and status by optionally evaluating against the ValueStack. This result can also be used to send and error to the client.
Parameters
status - the http servlet response status code that should be set on a response.
parse - true by default. If set to false, the headers param will not be parsed for Ognl expressions.
headers - header values.
error - the http servlet response error code that should be set on a response.
errorMessage - error message to be set on response if 'error' is set.
Examples
<result name="success" type="httpheader"> <param name="status">204</param> <param name="headers.a">a custom header value</param> <param name="headers.b">another custom header value</param> </result> <result name="proxyRequired" type="httpheader"> <param name="error">305</param> <param name="errorMessage">this action must be accessed through a prozy</param> </result>
Calls the {@link HttpServletResponse#sendRedirect(String) sendRedirect} method to the location specified. The response is told to redirect the browser to the specified location (a new request from the client). The consequence of doing this means that the action (action instance, action errors, field errors, etc) that was just executed is lost and no longer available. This is because actions are built on a single-thread model. The only way to pass data is through the session or with web parameters (url?name=value) which can be OGNL expressions.
Parameters
location (default) - the location to go to after execution.
parse - true by default. If set to false, the location param will not be parsed for Ognl expressions.
This result follows the same rules from StrutsResultSupport.
Examples
<result name="success" type="redirect"> <param name="location">foo.jsp</param> <param name="parse">false</param> </result>
<package name="passingRequestParameters" extends="struts-default" namespace="/passingRequestParameters"> <-- Pass parameters (reportType, width and height) --> <!-- The redirect-action url generated will be : /genReport/generateReport.jsp?reportType=pie&width=100&height=100 --> <action name="gatherReportInfo" class="..."> <result name="showReportResult" type="redirect"> <param name="location">generateReport.jsp</param> <param name="namespace">/genReport</param> <param name="reportType">pie</param> <param name="width">100</param> <param name="height">100</param> </result> </action> </package>
This result uses the ActionMapper provided by the ActionMapperFactory to redirect the browser to a URL that invokes the specified action and (optional) namespace. This is better than the ServletRedirectResult because it does not require you to encode the URL patterns processed by the ActionMapper in to your struts.xml configuration files. This means you can change your URL patterns at any point and your application will still work. It is strongly recommended that if you are redirecting to another action, you use this result rather than the standard redirect result.
See examples below for an example of how request parameters could be passed in.
See ActionMapper for more details |
Parameters
actionName (default) - the name of the action that will be redirect to
namespace - used to determine which namespace the action is in that we're redirecting to . If namespace is null, this defaults to the current namespace
Examples
<package name="public" extends="struts-default"> <action name="login" class="..."> <!-- Redirect to another namespace --> <result type="redirect-action"> <param name="actionName">dashboard</param> <param name="namespace">/secure</param> </result> </action> </package> <package name="secure" extends="struts-default" namespace="/secure"> <-- Redirect to an action in the same namespace --> <action name="dashboard" class="..."> <result>dashboard.jsp</result> <result name="error" type="redirect-action">error</result> </action> <action name="error" class="..."> <result>error.jsp</result> </action> </package> <package name="passingRequestParameters" extends="struts-default" namespace="/passingRequestParameters"> <-- Pass parameters (reportType, width and height) --> <!-- The redirect-action url generated will be : /genReport/generateReport.action?reportType=pie&width=100&height=100 --> <action name="gatherReportInfo" class="..."> <result name="showReportResult" type="redirect-action"> <param name="actionName">generateReport</param> <param name="namespace">/genReport</param> <param name="reportType">pie</param> <param name="width">100</param> <param name="height">100</param> </result> </action> </package>
A custom Result type for send raw data (via an InputStream) directly to the HttpServletResponse. Very useful for allowing users to download content.
If you are running your app server under HTTPS and having issues with PDF's or other file streams you should take a look at HTTPS and IE Issues |
Parameters
contentType - the stream mime-type as sent to the web browser (default =
text/plain
).
contentLength - the stream length in bytes (the browser displays a progress bar).
contentDispostion - the content disposition header value for specifing the file name (default =
inline
, values are typically
filename="document.pdf".
inputName - the name of the InputStream property from the chained action (default =
inputStream
).
bufferSize - the size of the buffer to copy from input to output (default =
1024
).
Examples
<result name="success" type="stream"> <param name="contentType">image/jpeg</param> <param name="inputName">imageStream</param> <param name="contentDisposition">filename="document.pdf"</param> <param name="bufferSize">1024</param> </result>
Using the Servlet container's JspFactory, this result mocks a JSP execution environment and then displays a Velocity template that will be streamed directly to the servlet output.
Parameters
location (default) - the location of the template to process.
parse - true by default. If set to false, the location param will not be parsed for Ognl expressions.
This result follows the same rules from StrutsResultSupport.
Examples
<result name="success" type="velocity"> <param name="location">foo.vm</param> </result>
XSLTResult uses XSLT to transform an action object to XML. The recent version has been specifically modified to deal with Xalan flaws. When using Xalan you may notice that even though you have a very minimal stylesheet like this one
<xsl:template match="/result"> <result/> </xsl:template>
Xalan would still iterate through every property of your action and all its descendants.
If you had double-linked objects, Xalan would work forever analysing an infinite object tree. Even if your stylesheet was not constructed to process them all. It's because the current Xalan eagerly and extensively converts everything to its internal DTM model before further processing.
That's why there's a loop eliminator added that works by indexing every object-property combination during processing. If it notices that some object's property was already walked through, it doesn't go any deeper. Say you have two objects, x and y, with the following properties set (pseudocode):
x.y = y; and y.x = x; action.x=x;
Due to that modification, the resulting XML document based on x would be:
<result> <x> <y/> </x> </result>
Without it there would be endless x/y/x/y/x/y/... elements.
The XSLTResult code tries also to deal with the fact that DTM model is built in a manner that children are processed before siblings. The result is that if there is object x that is both set in action's x property, and very deeply under action's a property then it would only appear under a, not under x. That's not what we expect, and that's why XSLTResult allows objects to repeat in various places to some extent.
Sometimes the object mesh is still very dense and you may notice that even though you have a relatively simple stylesheet, execution takes a tremendous amount of time. To help you to deal with that obstacle of Xalan, you may attach regexp filters to elements paths (xpath).
Note: In your .xsl file the root match must be named result.
This example will output the username by using getUsername on your action class:
<xsl:template match="result"> <html> <body> Hello <xsl:value-of select="username"/> how are you? </body> </html> <xsl:template/>
In the following example the XSLT result would only walk through action's properties without their childs. It would also skip every property that has "hugeCollection" in their name. Element's path is first compared to excludingPattern - if it matches it's no longer processed. Then it is compared to matchingPattern and processed only if there's a match.
<result name="success" type="xslt"> <param name="location">foo.xslt</param> <param name="matchingPattern">^/result/[^/*]$</param> <param name="excludingPattern">.*(hugeCollection).*</param> </result>
Parameters
location (default) - the location to go to after execution.
parse - true by default. If set to false, the location param will not be parsed for Ognl expressions.
matchingPattern - Pattern that matches only desired elements, by default it matches everything.
excludingPattern - Pattern that eliminates unwanted elements, by default it matches none.
struts.properties
related configuration:
struts.xslt.nocache - Defaults to false. If set to true, disables stylesheet caching. Good for development, bad for production.
Examples
<result name="success" type="xslt"> <param name="location">foo.xslt</param> <param name="matchingPattern">^/result/[^/*]$</param> <param name="excludingPattern">.*(hugeCollection).*</param> </result>
A result that send the content out as plain text. Usefull typically when needed to display the raw content of a JSP or Html file for example.
Parameters
- location (default) = location of the file (jsp/html) to be displayed as plain text.
- charSet (optional) = character set to be used. This character set will be used to set the response type (eg. Content-Type=text/plain; charset=UTF-8) and when reading using a Reader. Some example of charSet would be UTF-8, ISO-8859-1 etc.
Examples
<action name="displayJspRawContent" > <result type="plaintext">/myJspFile.jsp</result> </action> <action name="displayJspRawContent" > <result type="plaintext"> <param name="location">/myJspFile.jsp</param> <param name="charSet">UTF-8</param> </result> </action>
Tiles is a templating framework designed to easily allow the creation of web application pages with a consistent look and feel. It can be used for both page decorating and componentization. |
The Tiles plugin allows actions to return Tiles pages.
Features
- Supports Tiles in Freemarker, JSP, and Velocity
Usage
The following steps must be taken in order to enable tiles support within your Struts2 application:
- Include the struts-tiles-plugin as a dependency in your web application. If you are using maven2, the dependency configuration will be similar to:
<dependency> <groupId>org.apache.struts</groupId> <artifactId>struts2-tiles-plugin</artifactId> <version>${version.tiles}</version> <scope>compile</scope> </dependency>
- Register the tiles listener. This listener will typically either be the standard tiles listener (org.apache.tiles.listener.TilesListener) or the Struts2 replacement (org.apache.struts2.tiles.TilesListener). The latter provides tighter integration with Struts features such as freemarker integration.
<listener> <listener-class>org.apache.struts2.tiles.StrutsTilesListener</listener-class> </listener>
- All package definitions which require tiles support must either extend the tiles-default package or must register the Tiles Result type definition.
<result-types> <result-type name="tiles" class="org.apache.struts2.views.tiles.TilesResult"/> </result-types>
- Configure your actions to utilize a tiles definition:
<action name="sample" class="org.apache.struts2.tiles.example.SampleAction" > <result name="success" type="tiles">tilesWorks</result> </action>
Example
This example shows a Tiles layout page using Struts tags:
<%@ taglib uri="http://tiles.apache.org/tags-tiles" prefix="tiles" %> <%@ taglib prefix="s" uri="/struts-tags" %> <%-- Show usage; Used in Header --%> <tiles:importAttribute name="title" scope="request"/> <html> <head><title><tiles:getAsString name="title"/></title></head> <body> <tiles:insertAttribute name="header"/> <p id="body"> <tiles:insertAttribute name="body"/> </p> <p>Notice that this is a layout made in JSP</p> </body> </html>
Settings
This plugin does inherits settings from Tiles configuration.
Installation
This plugin can be installed by copying the plugin jar into your application's /WEB-INF/lib directory. No other files need to be copied or created.