/* Copyright 2005-2006 Tim Fennell
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.sourceforge.stripes.action;
import net.sourceforge.stripes.controller.AsyncResponse;
import net.sourceforge.stripes.controller.FlashScope;
import net.sourceforge.stripes.controller.StripesConstants;
import net.sourceforge.stripes.util.Log;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.IOException;
import java.util.Collection;
import java.util.HashSet;
/**
* <p>
* Resolution that uses the Servlet API to <em>redirect</em> the user to another
* path by issuing a client side redirect. Unlike the ForwardResolution the
* RedirectResolution can send the user to any URL anywhere on the web - though
* it is more commonly used to send the user to a location within the same
* application.</p>
*
* <p>
* By default the RedirectResolution will prepend the context path of the web
* application to any URL before redirecting the request. To prevent the context
* path from being prepended use the constructor:
* {@code RedirectResolution(String,boolean)}.</p>
*
* <p>
* It is also possible to append parameters to the URL to which the user will be
* redirected. This can be done by manually adding parameters with the
* addParameter() and addParameters() methods, and by invoking
* includeRequestParameters() which will cause all of the current request
* parameters to be included into the URL.</p>
*
* <p>
* The redirect type can be switched from a 302 temporary redirect (default) to
* a 301 permanent redirect using the setPermanent method.
* </p>
*
* @see ForwardResolution
* @author Tim Fennell
*/
public class RedirectResolution extends OnwardResolution<RedirectResolution> {
private static final Log log = Log.getInstance(RedirectResolution.class);
private boolean prependContext = true;
private boolean includeRequestParameters;
private Collection<ActionBean> beans; // used to flash action beans
private boolean permanent = false;
/**
* Simple constructor that takes the URL to which to forward the user.
* Defaults to prepending the context path to the url supplied before
* redirecting.
*
* @param url the URL to which the user's browser should be re-directed.
*/
public RedirectResolution(String url) {
this(url, true);
}
/**
* Constructor that allows explicit control over whether or not the context
* path is prepended to the URL before redirecting.
*
* @param url the URL to which the user's browser should be re-directed.
* @param prependContext true if the context should be prepended, false
* otherwise
*/
public RedirectResolution(String url, boolean prependContext) {
super(url);
this.prependContext = prependContext;
}
/**
* Constructs a RedirectResolution that will redirect to the URL appropriate
* for the ActionBean supplied. This constructor should be preferred when
* redirecting to an ActionBean as it will ensure the correct URL is always
* used.
*
* @param beanType the Class object representing the ActionBean to redirect
* to
*/
public RedirectResolution(Class<? extends ActionBean> beanType) {
super(beanType);
}
/**
* Constructs a RedirectResolution that will redirect to the URL appropriate
* for the ActionBean supplied. This constructor should be preferred when
* redirecting to an ActionBean as it will ensure the correct URL is always
* used.
*
* @param beanType the Class object representing the ActionBean to redirect
* to
* @param event the event that should be triggered on the redirect
*/
public RedirectResolution(Class<? extends ActionBean> beanType, String event) {
super(beanType, event);
}
/**
* This method is overridden to make it public.
*/
@Override
public String getAnchor() {
return super.getAnchor();
}
/**
* This method is overridden to make it public.
*/
@Override
public RedirectResolution setAnchor(String anchor) {
return super.setAnchor(anchor);
}
/**
* If set to true, will cause absolutely all request parameters present in
* the current request to be appended to the redirect URL that will be sent
* to the browser. Since some browsers and servers cannot handle extremely
* long URLs, care should be taken when using this method with large form
* posts.
*
* @param inc whether or not current request parameters should be included
* in the redirect
* @return RedirectResolution, this resolution so that methods can be
* chained
*/
public RedirectResolution includeRequestParameters(boolean inc) {
this.includeRequestParameters = inc;
return this;
}
/**
* Causes the ActionBean supplied to be added to the Flash scope and made
* available during the next request cycle.
*
* @param bean the ActionBean to be added to flash scope
* @return The redirect resolution with the actionbean added to Flash scope
* @since Stripes 1.2
*/
public RedirectResolution flash(ActionBean bean) {
if (this.beans == null) {
this.beans = new HashSet<ActionBean>();
}
this.beans.add(bean);
return this;
}
/**
* Attempts to redirect the user to the specified URL.
*
* @throws ServletException thrown when the Servlet container encounters an
* error
* @throws IOException thrown when the Servlet container encounters an error
*/
@SuppressWarnings("unchecked")
public void execute(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
if (permanent) {
response.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY);
response = new HttpServletResponseWrapper(response) {
@Override
public void setStatus(int sc) {
}
@Override
public void sendRedirect(String location) throws IOException {
setHeader("Location", location);
}
};
}
if (this.includeRequestParameters) {
addParameters(request.getParameterMap());
}
// Add any beans to the flash scope
if (this.beans != null) {
FlashScope flash = FlashScope.getCurrent(request, true);
for (ActionBean bean : this.beans) {
flash.put(bean);
}
}
// If a flash scope exists, add the parameter to the request
FlashScope flash = FlashScope.getCurrent(request, false);
if (flash != null) {
addParameter(StripesConstants.URL_KEY_FLASH_SCOPE_ID, flash.key());
}
// Prepend the context path if requested
String url = getUrl(request.getLocale());
if (prependContext) {
String contextPath = request.getContextPath();
if (contextPath.length() > 1) {
url = contextPath + url;
}
}
url = response.encodeRedirectURL(url);
log.trace("Redirecting ", this.beans == null ? "" : "(w/flashed bean) ", "to URL: ", url);
response.sendRedirect(url);
AsyncResponse asyncResponse = AsyncResponse.get(request);
if (asyncResponse != null) {
// async started, complete
asyncResponse.complete();
}
}
/**
* Sets the redirect type to permanent (301) instead of temporary (302).
*
* @param permanent - Whether or not this resolution should have the permanent HTTP status set on it.
* @return RedirectResolution with the permanent header status code set on it.
*/
public RedirectResolution setPermanent(boolean permanent) {
this.permanent = permanent;
return this;
}
}