1 /*
2 * $Id: StrutsResultSupport.java 651946 2008-04-27 13:41:38Z apetrelli $
3 *
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
19 * under the License.
20 */
21
22 package org.apache.struts2.dispatcher;
23
24 import java.io.UnsupportedEncodingException;
25 import java.net.URLEncoder;
26
27 import org.apache.struts2.StrutsStatics;
28
29 import com.opensymphony.xwork2.ActionInvocation;
30 import com.opensymphony.xwork2.Result;
31 import com.opensymphony.xwork2.util.TextParseUtil;
32 import com.opensymphony.xwork2.util.logging.Logger;
33 import com.opensymphony.xwork2.util.logging.LoggerFactory;
34
35
36 /**
37 * <!-- START SNIPPET: javadoc -->
38 *
39 * A base class for all Struts action execution results.
40 * The "location" param is the default parameter, meaning the most common usage of this result would be:
41 * <p/>
42 * This class provides two common parameters for any subclass:
43 * <ul>
44 * <li>location - the location to go to after execution (could be a jsp page or another action).
45 * It can be parsed as per the rules definied in the
46 * {@link TextParseUtil#translateVariables(java.lang.String, com.opensymphony.xwork2.util.ValueStack) translateVariables}
47 * method</li>
48 * <li>parse - true by default. If set to false, the location param will not be parsed for expressions</li>
49 * <li>encode - false by default. If set to false, the location param will not be url encoded. This only have effect when parse is true</li>
50 * </ul>
51 *
52 * <b>NOTE:</b>
53 * The encode param will only have effect when parse is true
54 *
55 * <!-- END SNIPPET: javadoc -->
56 *
57 * <p/>
58 *
59 * <!-- START SNIPPET: example -->
60 *
61 * <p/>
62 * In the struts.xml configuration file, these would be included as:
63 * <p/>
64 * <pre>
65 * <result name="success" type="redirect">
66 * <param name="<b>location</b>">foo.jsp</param>
67 * </result></pre>
68 * <p/>
69 * or
70 * <p/>
71 * <pre>
72 * <result name="success" type="redirect" >
73 * <param name="<b>location</b>">foo.jsp?url=${myUrl}</param>
74 * <param name="<b>parse</b>">true</param>
75 * <param name="<b>encode</b>">true</param>
76 * </result></pre>
77 * <p/>
78 * In the above case, myUrl will be parsed against Ognl Value Stack and then
79 * URL encoded.
80 * <p/>
81 * or when using the default parameter feature
82 * <p/>
83 * <pre>
84 * <result name="success" type="redirect"><b>foo.jsp</b></result></pre>
85 * <p/>
86 * You should subclass this class if you're interested in adding more parameters or functionality
87 * to your Result. If you do subclass this class you will need to
88 * override {@link #doExecute(String, ActionInvocation)}.<p>
89 * <p/>
90 * Any custom result can be defined in struts.xml as:
91 * <p/>
92 * <pre>
93 * <result-types>
94 * ...
95 * <result-type name="myresult" class="com.foo.MyResult" />
96 * </result-types></pre>
97 * <p/>
98 * Please see the {@link com.opensymphony.xwork2.Result} class for more info on Results in general.
99 *
100 * <!-- END SNIPPET: example -->
101 *
102 * @see com.opensymphony.xwork2.Result
103 */
104 public abstract class StrutsResultSupport implements Result, StrutsStatics {
105
106 private static final Logger LOG = LoggerFactory.getLogger(StrutsResultSupport.class);
107
108 /** The default parameter */
109 public static final String DEFAULT_PARAM = "location";
110
111 private boolean parse;
112 private boolean encode;
113 private String location;
114 private String lastFinalLocation;
115
116 public StrutsResultSupport() {
117 this(null, true, false);
118 }
119
120 public StrutsResultSupport(String location) {
121 this(location, true, false);
122 }
123
124 public StrutsResultSupport(String location, boolean parse, boolean encode) {
125 this.location = location;
126 this.parse = parse;
127 this.encode = encode;
128 }
129
130 /**
131 * The location to go to after action execution. This could be a JSP page or another action.
132 * The location can contain OGNL expressions which will be evaulated if the <tt>parse</tt>
133 * parameter is set to <tt>true</tt>.
134 *
135 * @param location the location to go to after action execution.
136 * @see #setParse(boolean)
137 */
138 public void setLocation(String location) {
139 this.location = location;
140 }
141
142 /**
143 * Gets the location it was created with, mainly for testing
144 */
145 public String getLocation() {
146 return location;
147 }
148
149 /**
150 * Returns the last parsed and encoded location value
151 */
152 public String getLastFinalLocation() {
153 return lastFinalLocation;
154 }
155
156 /**
157 * Set parse to <tt>true</tt> to indicate that the location should be parsed as an OGNL expression. This
158 * is set to <tt>true</tt> by default.
159 *
160 * @param parse <tt>true</tt> if the location parameter is an OGNL expression, <tt>false</tt> otherwise.
161 */
162 public void setParse(boolean parse) {
163 this.parse = parse;
164 }
165
166 /**
167 * Set encode to <tt>true</tt> to indicate that the location should be url encoded. This is set to
168 * <tt>true</tt> by default
169 *
170 * @param encode <tt>true</tt> if the location parameter should be url encode, <tt>false</tt> otherwise.
171 */
172 public void setEncode(boolean encode) {
173 this.encode = encode;
174 }
175
176 /**
177 * Implementation of the <tt>execute</tt> method from the <tt>Result</tt> interface. This will call
178 * the abstract method {@link #doExecute(String, ActionInvocation)} after optionally evaluating the
179 * location as an OGNL evaluation.
180 *
181 * @param invocation the execution state of the action.
182 * @throws Exception if an error occurs while executing the result.
183 */
184 public void execute(ActionInvocation invocation) throws Exception {
185 lastFinalLocation = conditionalParse(location, invocation);
186 doExecute(lastFinalLocation, invocation);
187 }
188
189 /**
190 * Parses the parameter for OGNL expressions against the valuestack
191 *
192 * @param param The parameter value
193 * @param invocation The action invocation instance
194 * @return The resulting string
195 */
196 protected String conditionalParse(String param, ActionInvocation invocation) {
197 if (parse && param != null && invocation != null) {
198 return TextParseUtil.translateVariables(param, invocation.getStack(),
199 new TextParseUtil.ParsedValueEvaluator() {
200 public Object evaluate(Object parsedValue) {
201 if (encode) {
202 if (parsedValue != null) {
203 try {
204 // use UTF-8 as this is the recommended encoding by W3C to
205 // avoid incompatibilities.
206 return URLEncoder.encode(parsedValue.toString(), "UTF-8");
207 }
208 catch(UnsupportedEncodingException e) {
209 LOG.warn("error while trying to encode ["+parsedValue+"]", e);
210 }
211 }
212 }
213 return parsedValue;
214 }
215 });
216 } else {
217 return param;
218 }
219 }
220
221 /**
222 * Executes the result given a final location (jsp page, action, etc) and the action invocation
223 * (the state in which the action was executed). Subclasses must implement this class to handle
224 * custom logic for result handling.
225 *
226 * @param finalLocation the location (jsp page, action, etc) to go to.
227 * @param invocation the execution state of the action.
228 * @throws Exception if an error occurs while executing the result.
229 */
230 protected abstract void doExecute(String finalLocation, ActionInvocation invocation) throws Exception;
231 }