View Javadoc

1   /*
2    * $Id: ServletRedirectResult.java 781086 2009-06-02 17:26:17Z wesw $
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 javax.servlet.http.HttpServletRequest;
25  import javax.servlet.http.HttpServletResponse;
26  import static javax.servlet.http.HttpServletResponse.*;
27  
28  import org.apache.struts2.ServletActionContext;
29  import org.apache.struts2.views.util.UrlHelper;
30  import org.apache.struts2.dispatcher.mapper.ActionMapper;
31  import org.apache.struts2.dispatcher.mapper.ActionMapping;
32  
33  import com.opensymphony.xwork2.ActionContext;
34  import com.opensymphony.xwork2.ActionInvocation;
35  import com.opensymphony.xwork2.config.entities.ResultConfig;
36  import com.opensymphony.xwork2.inject.Inject;
37  import com.opensymphony.xwork2.util.logging.Logger;
38  import com.opensymphony.xwork2.util.logging.LoggerFactory;
39  import com.opensymphony.xwork2.util.reflection.ReflectionException;
40  import com.opensymphony.xwork2.util.reflection.ReflectionExceptionHandler;
41  
42  import java.io.IOException;
43  import java.util.Map;
44  import java.util.LinkedHashMap;
45  import java.util.Iterator;
46  import java.util.List;
47  import java.util.Arrays;
48  
49  
50  /***
51   * <!-- START SNIPPET: description -->
52   *
53   * Calls the {@link HttpServletResponse#sendRedirect(String) sendRedirect}
54   * method to the location specified. The response is told to redirect the
55   * browser to the specified location (a new request from the client). The
56   * consequence of doing this means that the action (action instance, action
57   * errors, field errors, etc) that was just executed is lost and no longer
58   * available. This is because actions are built on a single-thread model. The
59   * only way to pass data is through the session or with web parameters
60   * (url?name=value) which can be OGNL expressions.
61   *
62   * <!-- END SNIPPET: description -->
63   * <p/>
64   * <b>This result type takes the following parameters:</b>
65   *
66   * <!-- START SNIPPET: params -->
67   *
68   * <ul>
69   *
70   * <li><b>location (default)</b> - the location to go to after execution.</li>
71   *
72   * <li><b>parse</b> - true by default. If set to false, the location param will
73   * not be parsed for Ognl expressions.</li>
74   *
75   * </ul>
76   *
77   * <p>
78   * This result follows the same rules from {@link StrutsResultSupport}.
79   * </p>
80   *
81   * <!-- END SNIPPET: params -->
82   *
83   * <b>Example:</b>
84   *
85   * <pre><!-- START SNIPPET: example -->
86   * &lt;result name="success" type="redirect"&gt;
87   *   &lt;param name="location"&gt;foo.jsp&lt;/param&gt;
88   *   &lt;param name="parse"&gt;false&lt;/param&gt;
89   * &lt;/result&gt;
90   * <!-- END SNIPPET: example --></pre>
91   *
92   */
93  public class ServletRedirectResult extends StrutsResultSupport implements ReflectionExceptionHandler {
94  
95      private static final long serialVersionUID = 6316947346435301270L;
96  
97      private static final Logger LOG = LoggerFactory.getLogger(ServletRedirectResult.class);
98  
99      protected boolean prependServletContext = true;
100 
101     protected ActionMapper actionMapper;
102 
103     protected int statusCode = SC_FOUND;
104 
105     protected boolean supressEmptyParameters = false;
106 
107     protected Map<String, String> requestParameters = new LinkedHashMap<String, String>();
108 
109     public ServletRedirectResult() {
110         super();
111     }
112 
113     public ServletRedirectResult(String location) {
114         super(location);
115     }
116     
117     @Inject
118     public void setActionMapper(ActionMapper mapper) {
119         this.actionMapper = mapper;
120     }
121 
122     public void setStatusCode(int code) {
123         this.statusCode = code;
124     }
125 
126     /***
127      * Sets whether or not to prepend the servlet context path to the redirected URL.
128      *
129      * @param prependServletContext <tt>true</tt> to prepend the location with the servlet context path,
130      *                              <tt>false</tt> otherwise.
131      */
132     public void setPrependServletContext(boolean prependServletContext) {
133         this.prependServletContext = prependServletContext;
134     }
135 
136     /***
137      * Redirects to the location specified by calling {@link HttpServletResponse#sendRedirect(String)}.
138      *
139      * @param finalLocation the location to redirect to.
140      * @param invocation    an encapsulation of the action execution state.
141      * @throws Exception if an error occurs when redirecting.
142      */
143     protected void doExecute(String finalLocation, ActionInvocation invocation) throws Exception {
144         ActionContext ctx = invocation.getInvocationContext();
145         HttpServletRequest request = (HttpServletRequest) ctx.get(ServletActionContext.HTTP_REQUEST);
146         HttpServletResponse response = (HttpServletResponse) ctx.get(ServletActionContext.HTTP_RESPONSE);
147 
148         if (isPathUrl(finalLocation)) {
149             if (!finalLocation.startsWith("/")) {
150                 ActionMapping mapping = actionMapper.getMapping(
151                         request, Dispatcher.getInstance().getConfigurationManager()); 
152                 String namespace = null;
153                 if (mapping != null) {
154                     namespace = mapping.getNamespace();
155                 }
156 
157                 if ((namespace != null) && (namespace.length() > 0) && (!"/".equals(namespace))) {
158                     finalLocation = namespace + "/" + finalLocation;
159                 } else {
160                     finalLocation = "/" + finalLocation;
161                 }
162             }
163 
164             // if the URL's are relative to the servlet context, append the servlet context path
165             if (prependServletContext && (request.getContextPath() != null) && (request.getContextPath().length() > 0)) {
166                 finalLocation = request.getContextPath() + finalLocation;
167             }
168 
169             ResultConfig resultConfig = invocation.getProxy().getConfig().getResults().get(invocation.getResultCode());
170             if (resultConfig != null ) {
171                 Map resultConfigParams = resultConfig.getParams();
172                 for (Iterator i = resultConfigParams.entrySet().iterator(); i.hasNext();) {
173                     Map.Entry e = (Map.Entry) i.next();
174 
175                     if (!getProhibitedResultParams().contains(e.getKey())) {
176                         requestParameters.put(e.getKey().toString(),
177                                 e.getValue() == null ? "" :
178                                         conditionalParse(e.getValue().toString(), invocation));
179                         String potentialValue = e.getValue() == null ? "" : conditionalParse(e.getValue().toString(), invocation);
180                         if (!supressEmptyParameters || ((potentialValue != null) && (potentialValue.length() > 0))) {
181                             requestParameters.put(e.getKey().toString(), potentialValue);
182                         }
183                     }
184                 }
185             }
186 
187             StringBuilder tmpLocation = new StringBuilder(finalLocation);
188             UrlHelper.buildParametersString(requestParameters, tmpLocation, "&");
189 
190             finalLocation = response.encodeRedirectURL(tmpLocation.toString());
191         }
192 
193         if (LOG.isDebugEnabled()) {
194             LOG.debug("Redirecting to finalLocation " + finalLocation);
195         }
196 
197         sendRedirect(response, finalLocation);
198     }
199 
200     protected List<String> getProhibitedResultParams() {
201         return Arrays.asList(new String[]{
202                 DEFAULT_PARAM, "namespace", "method", "encode", "parse", "location",
203                 "prependServletContext", "supressEmptyParameters"});
204     }
205 
206 
207     /***
208      * Sends the redirection.  Can be overridden to customize how the redirect is handled (i.e. to use a different
209      * status code)
210      *
211      * @param response The response
212      * @param finalLocation The location URI
213      * @throws IOException
214      */
215     protected void sendRedirect(HttpServletResponse response, String finalLocation) throws IOException {
216         if (SC_FOUND == statusCode) {
217             response.sendRedirect(finalLocation);
218         } else {
219             response.setStatus(statusCode);
220             response.setHeader("Location", finalLocation);
221             response.getWriter().write(finalLocation);
222             response.getWriter().close();
223         }
224 
225     }
226 
227     private static boolean isPathUrl(String url) {
228         // filter out "http:", "https:", "mailto:", "file:", "ftp:"
229         // since the only valid places for : in URL's is before the path specification
230         // either before the port, or after the protocol
231         return (url.indexOf(':') == -1);
232     }
233 
234     /***
235      * Sets the supressEmptyParameters option
236      *
237      * @param supressEmptyParameters The new value for this option
238      */
239     public void setSupressEmptyParameters(boolean supressEmptyParameters) {
240         this.supressEmptyParameters = supressEmptyParameters;
241     }
242 
243     /***
244      * Adds a request parameter to be added to the redirect url
245      *
246      * @param key The parameter name
247      * @param value The parameter value
248      */
249     public ServletRedirectResult addParameter(String key, Object value) {
250         requestParameters.put(key, String.valueOf(value));
251         return this;
252     }
253 
254     public void handle(ReflectionException ex) {
255         // Only log as debug as they are probably parameters to be appended to the url
256         LOG.debug(ex.getMessage(), ex);
257     }
258 }