/* 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 java.util.ArrayList; import java.util.List; import java.util.Locale; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import net.sourceforge.stripes.controller.FlashScope; import net.sourceforge.stripes.controller.StripesConstants; import net.sourceforge.stripes.exception.SourcePageNotFoundException; import net.sourceforge.stripes.util.CryptoUtil; import net.sourceforge.stripes.validation.ValidationErrors; /** * <p>Encapsulates information about the current request. Also provides access to the underlying * Servlet API should you need to use it for any reason.</p> * * <p>Developers should generally consider subclassing ActionBeanContext to provide a facade * to contextual state for their application. Type safe getters and setter can be added to * the subclass and used by the application, thus hiding where the information is actually * stored. This approach is documented in more detail in the Stripes documentation on * <a href="http://stripesframework.org/display/stripes/State+Management">State Management</a>.</p> * * @author Tim Fennell */ public class ActionBeanContext { private HttpServletRequest request; private HttpServletResponse response; private ServletContext servletContext; private String eventName; private ValidationErrors validationErrors; /** * Retrieves the HttpServletRequest object that is associated with the current request. * @return HttpServletRequest the current request */ public HttpServletRequest getRequest() { return request; } /** * Used by the DispatcherServlet to set the HttpServletRequest for the current request * @param request the current request */ public void setRequest(HttpServletRequest request) { this.request = request; } /** * Retrieves the HttpServletResponse that is associated with the current request. * @return HttpServletResponse the current response */ public HttpServletResponse getResponse() { return response; } /** * Used by the DispatcherServlet to set the HttpServletResponse that is associated with * the current request. * @param response the current response */ public void setResponse(HttpServletResponse response) { this.response = response; } /** * Retrieves the ServletContext object that is associated with the context in which the * current request is being processed. * @return ServletContext the current ServletContext */ public ServletContext getServletContext() { return servletContext; } /** * Sets the ServletContext object that is associated with the context in which the * current request is being processed. * @param servletContext the current ServletContext */ public void setServletContext(ServletContext servletContext) { this.servletContext = servletContext; } /** * Supplies the name of the event being handled. While a specific method is usually invoked on * an ActionBean, through the use of default handlers ambiguity can arise. This allows * ActionBeans to definitively know the name of the event that was fired. * * @return String the name of the event being handled */ public String getEventName() { return eventName; } /** * Used by the DispatcherServlet to set the name of the even being handled. * @param eventName the name of the event being handled */ public void setEventName(String eventName) { this.eventName = eventName; } /** * Returns the set of validation errors associated with the current form. Lazily * initialized the set of errors, and will never return null. * * @return a Collection of validation errors */ public ValidationErrors getValidationErrors() { if (this.validationErrors == null) { this.validationErrors = new ValidationErrors(); } return validationErrors; } /** * Replaces the current set of validation errors. * @param validationErrors a collect of validation errors */ public void setValidationErrors(ValidationErrors validationErrors) { this.validationErrors = validationErrors; } /** * <p>Returns the default set of non-error messages associated with the current request. * Guaranteed to always return a List, though the list may be empty. It is envisaged that * messages will normally be added to the request as follows:</p> * *<pre> *getContext().getMessages().add( ... ); *</pre> * * <p>To remove messages from the current request fetch the list of messages and invoke * remove() or clear(). Messages will be made available to JSPs during the current * request and in the subsequent request if a redirect is issued.</p> * * @return a List of Message objects associated with the current request, never null. * @see ActionBeanContext#getMessages(String) */ public List<Message> getMessages() { return getMessages(StripesConstants.REQ_ATTR_MESSAGES); } /** * <p>Returns the set of non-error messages associated with the current request under the * specified key. Can be used to manage multiple lists of messages, for different purposes. * Guaranteed to always return a List, though the list may be empty. It is envisaged that * messages will normally be added to the request as follows:</p> * *<pre> *getContext().getMessages(key).add( ... ); *</pre> * * <p>To remove messages from the current request fetch the list of messages and invoke * remove() or clear().</p> * * <p>Messages are stored in a {@link net.sourceforge.stripes.controller.FlashScope} for * the current request. This means that they are available in request scope using the * supplied key during both this request, and the subsequent request if it is the result * of a redirect.</p> * * @return a List of Message objects associated with the current request, never null. */ @SuppressWarnings("unchecked") public List<Message> getMessages(String key) { FlashScope scope = FlashScope.getCurrent(getRequest(), true); List<Message> messages = (List<Message>) scope.get(key); if (messages == null) { messages = new ArrayList<Message>(); /* * Messages imported from previous flash scope will be present in request scope but not * in current flash scope. Handle such cases by copying the existing messages to a new * list in the current flash and request scopes. */ if (getRequest().getAttribute(key) instanceof List) { try { for (Message message : ((List<Message>) getRequest().getAttribute(key))) { messages.add(message); } } catch (ClassCastException e) { messages.clear(); } } scope.put(key, messages); } return messages; } /** * Gets the Locale that is being used to service the current request. This is *not* the value * that was submitted in the request, but the value picked by the configured LocalePicker * which takes into consideration the locales preferred in the request. * * @return Locale the locale being used for the current request * @see net.sourceforge.stripes.localization.LocalePicker */ public Locale getLocale() { return this.request.getLocale(); } /** * <p>Returns a resolution that can be used to return the user to the page from which they * submitted they current request. Most useful in situations where a user-correctable error * has occurred that was too difficult or expensive to check at validation time. In that case * an ActionBean can call setValidationErrors() and then return the resolution provided by * this method.</p> * * @return Resolution a resolution that will forward the user to the page they came from * @throws SourcePageNotFoundException if the information required to construct a source page * resolution cannot be found in the request. * @see #getSourcePage() */ public Resolution getSourcePageResolution() throws SourcePageNotFoundException { String sourcePage = getSourcePage(); if (sourcePage == null) { throw new SourcePageNotFoundException(this); } else { return new ForwardResolution(sourcePage); } } /** * <p> * Returns the context-relative path to the page from which the user submitted they current * request. * </p> * * @return Resolution a resolution that will forward the user to the page they came from * @throws IllegalStateException if the information required to construct a source page * resolution cannot be found in the request. * @see #getSourcePageResolution() */ public String getSourcePage() { String sourcePage = request.getParameter(StripesConstants.URL_KEY_SOURCE_PAGE); if (sourcePage != null) { sourcePage = CryptoUtil.decrypt(sourcePage); } return sourcePage; } /** * Returns a String with the name of the event for which the instance holds context, and * the set of validation errors, if any. */ @Override public String toString() { return getClass().getName() + "{" + "eventName='" + eventName + "'" + ", validationErrors=" + validationErrors + "}"; } }