package net.sourceforge.stripes.action;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
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.controller.StripesFilter;
import net.sourceforge.stripes.tag.ErrorsTag;
import net.sourceforge.stripes.util.CryptoUtil;
import net.sourceforge.stripes.util.Log;
import net.sourceforge.stripes.validation.ValidationError;
import net.sourceforge.stripes.validation.ValidationErrors;
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>();
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 IllegalStateException if the information required to construct a source page
* resolution cannot be found in the request.
* @see #getSourcePage()
*/
public Resolution getSourcePageResolution() {
String sourcePage = getSourcePage();
if (sourcePage == null) {
return new ValidationErrorReportResolution(getValidationErrors());
}
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 "ActionBeanContext{" +
"eventName='" + eventName + "'" +
", validationErrors=" + validationErrors +
"}";
}
}
class ValidationErrorReportResolution implements Resolution {
private static final Log log = Log.getInstance(ValidationErrorReportResolution.class);
private ValidationErrors errors;
protected ValidationErrorReportResolution(ValidationErrors errors) {
this.errors = errors;
}
public void execute(HttpServletRequest request, HttpServletResponse response) throws Exception {
// log an exception for the stack trace
Exception exception = new IllegalStateException(
"Here's how it is. Someone (quite possibly the Stripes Dispatcher) needed " +
"to get the source page resolution. But no source page was supplied in the " +
"request, and unless you override ActionBeanContext.getSourcePageResolution() " +
"you're going to need that value. When you use a <stripes:form> tag a hidden " +
"field called '" + StripesConstants.URL_KEY_SOURCE_PAGE + "' is included. " +
"If you write your own forms or links that could generate validation errors, " +
"you must include a value for this parameter. This can be done by calling " +
"request.getServletPath().");
log.error(exception);
// start the HTML error report
response.setContentType("text/html");
PrintWriter writer = response.getWriter();
writer.println("<html>");
writer.println("<head><title>Stripes validation error report</title></head>");
writer.println("<body style=\"font-family: Arial, sans-serif; font-size: 10pt;\">");
writer.println("<h1>Stripes validation error report</h1><p>");
writer.println(exception.getMessage());
writer.println("</p><h2>Validation errors</h2><p>");
sendErrors(request, response);
writer.println("</p></body></html>");
}
protected void sendErrors(HttpServletRequest request, HttpServletResponse response)
throws Exception {
// Output all errors in a standard format
Locale locale = request.getLocale();
ResourceBundle bundle = null;
try {
bundle = StripesFilter.getConfiguration().getLocalizationBundleFactory()
.getErrorMessageBundle(locale);
}
catch (MissingResourceException mre) {
log.warn(getClass().getName(), " could not find the error messages resource bundle. ",
"As a result default headers/footers etc. will be used. Check that ",
"you have a StripesResources.properties in your classpath (unless ",
"of course you have configured a different bundle).");
}
// Fetch the header and footer
String header = getResource(bundle, "header", ErrorsTag.DEFAULT_HEADER);
String footer = getResource(bundle, "footer", ErrorsTag.DEFAULT_FOOTER);
String openElement = getResource(bundle, "beforeError", "<li>");
String closeElement = getResource(bundle, "afterError", "</li>");
// Write out the error messages
PrintWriter writer = response.getWriter();
writer.write(header);
for (List<ValidationError> list : errors.values()) {
for (ValidationError fieldError : list) {
writer.write(openElement);
writer.write(fieldError.getMessage(locale));
writer.write(closeElement);
}
}
writer.write(footer);
}
/**
* Utility method that is used to lookup the resources used for the errors header,
* footer, and the strings that go before and after each error.
*
* @param bundle the bundle to look up the resource from
* @param name the name of the resource to lookup (prefixes will be added)
* @param fallback a value to return if no resource can be found
* @return the value to use for the named resource
*/
protected String getResource(ResourceBundle bundle, String name, String fallback) {
if (bundle == null) {
return fallback;
}
String resource;
try { resource = bundle.getString("stripes.errors." + name); }
catch (MissingResourceException mre) { resource = fallback; }
return resource;
}
}
在alidate property in form, 我们要把错误信息存到validationErrors。