/* Copyright 2010 Ben Gunter
*
* 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.io.PrintWriter;
import java.util.List;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import net.sourceforge.stripes.controller.AsyncResponse;
import net.sourceforge.stripes.controller.StripesFilter;
import net.sourceforge.stripes.exception.SourcePageNotFoundException;
import net.sourceforge.stripes.tag.ErrorsTag;
import net.sourceforge.stripes.util.HtmlUtil;
import net.sourceforge.stripes.util.Log;
import net.sourceforge.stripes.validation.ValidationError;
/**
* A resolution that streams a simple HTML response to the client detailing the validation errors
* that apply for an {@link ActionBeanContext}.
*
* @author Ben Gunter
* @since Stripes 1.5.5
*/
public class ValidationErrorReportResolution implements Resolution {
private static final Log log = Log.getInstance(ValidationErrorReportResolution.class);
private ActionBeanContext context;
/** Construct a new instance to report validation errors in the specified context. */
public ValidationErrorReportResolution(ActionBeanContext context) {
this.context = context;
}
/** Get the action bean context on which the validation errors occurred. */
public ActionBeanContext getContext() {
return context;
}
public void execute(HttpServletRequest request, HttpServletResponse response) throws Exception {
// log an exception for the stack trace
SourcePageNotFoundException exception = new SourcePageNotFoundException(getContext());
log.error(exception);
// start the HTML error report
response.setContentType("text/html");
PrintWriter writer = response.getWriter();
writer.println("<div style=\"font-family: Arial, sans-serif; font-size: 10pt;\">");
writer.println("<h1>Stripes validation error report</h1><p>");
writer.println(HtmlUtil.encode(exception.getMessage()));
writer.println("</p><h2>Validation errors</h2><p>");
sendErrors(request, response);
writer.println("</p></div>");
AsyncResponse asyncResponse = AsyncResponse.get(request);
if (asyncResponse != null) {
// async started, complete
asyncResponse.complete();
}
}
/**
* Called by {@link #execute(HttpServletRequest, HttpServletResponse)} to write the actual
* validation errors to the client. The {@code header}, {@code footer}, {@code beforeError} and
* {@code afterError} resources are used by this method.
*
* @param request The servlet request.
* @param response The servlet response.
*/
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 : getContext().getValidationErrors().values()) {
for (ValidationError fieldError : list) {
writer.write(openElement);
writer.write(HtmlUtil.encode(fieldError.getMessage(locale)));
writer.write(closeElement);
}
}
writer.write(footer);
}
/**
* Utility method that is used to lookup the resources used for the error 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;
}
}