Revision: 918
http://stripes.svn.sourceforge.net/stripes/?rev=918&view=rev
Author: bengunter
Date: 2008-05-20 12:59:53 -0700 (Tue, 20 May 2008)
Log Message:
-----------
Fixed STS-571. DefaultExceptionHandler now handles
FileUploadedLimitExceededException with some grace. This implementation is very
similar to FULEECatcher.java that I posted to the mailing list a while back.
Modified Paths:
--------------
trunk/stripes/resources/StripesResources.properties
trunk/stripes/src/net/sourceforge/stripes/exception/DefaultExceptionHandler.java
Modified: trunk/stripes/resources/StripesResources.properties
===================================================================
--- trunk/stripes/resources/StripesResources.properties 2008-05-20 15:32:56 UTC
(rev 917)
+++ trunk/stripes/resources/StripesResources.properties 2008-05-20 19:59:53 UTC
(rev 918)
@@ -64,3 +64,4 @@
validation.maxvalue.valueAboveMaximum=The maximum allowed value for {0} is {2}
validation.mask.valueDoesNotMatch=<em>{1}</em> is not a valid {0}
validation.expression.valueFailedExpression=The value supplied ({1}) for field
{0} is invalid
+validation.file.postBodyTooBig=Total upload size of {3} KB exceeds the maximum
size of {2} KB
Modified:
trunk/stripes/src/net/sourceforge/stripes/exception/DefaultExceptionHandler.java
===================================================================
---
trunk/stripes/src/net/sourceforge/stripes/exception/DefaultExceptionHandler.java
2008-05-20 15:32:56 UTC (rev 917)
+++
trunk/stripes/src/net/sourceforge/stripes/exception/DefaultExceptionHandler.java
2008-05-20 19:59:53 UTC (rev 918)
@@ -14,19 +14,35 @@
*/
package net.sourceforge.stripes.exception;
-import net.sourceforge.stripes.action.Resolution;
-import net.sourceforge.stripes.config.Configuration;
-import net.sourceforge.stripes.util.Log;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import java.beans.BeanInfo;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
+import java.net.URL;
+import java.text.DecimalFormat;
import java.util.HashMap;
import java.util.Map;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import net.sourceforge.stripes.action.ActionBean;
+import net.sourceforge.stripes.action.ActionBeanContext;
+import net.sourceforge.stripes.action.FileBean;
+import net.sourceforge.stripes.action.RedirectResolution;
+import net.sourceforge.stripes.action.Resolution;
+import net.sourceforge.stripes.config.Configuration;
+import net.sourceforge.stripes.controller.DispatcherHelper;
+import net.sourceforge.stripes.controller.ExecutionContext;
+import net.sourceforge.stripes.controller.FileUploadLimitExceededException;
+import net.sourceforge.stripes.controller.StripesConstants;
+import net.sourceforge.stripes.controller.StripesRequestWrapper;
+import net.sourceforge.stripes.util.Log;
+import net.sourceforge.stripes.validation.LocalizableError;
+
/**
* <p>Default ExceptionHandler implementation that makes it easy for users to
extend and
* add custom handling for different types of exception. When extending this
class methods
@@ -123,6 +139,12 @@
if (proxy != null) {
proxy.handle(actual, request, response);
}
+ else if (throwable instanceof FileUploadLimitExceededException) {
+ Resolution resolution =
handle((FileUploadLimitExceededException) throwable,
+ request, response);
+ if (resolution != null)
+ resolution.execute(request, response);
+ }
else {
// If there's no sensible proxy, rethrow the original
throwable,
// NOT the unwrapped one since they may add extra information
@@ -137,6 +159,157 @@
}
}
+ /**
+ * <p>
+ * [EMAIL PROTECTED] FileUploadLimitExceededException} is notoriously
difficult to handle for several
+ * reasons:
+ * <ul>
+ * <li>The exception is thrown during construction of the [EMAIL
PROTECTED] StripesRequestWrapper}. Many
+ * Stripes components rely on the presence of this wrapper, yet it cannot
be created normally.</li>
+ * <li>It happens before the request lifecycle has begun. There is no
[EMAIL PROTECTED] ExecutionContext},
+ * [EMAIL PROTECTED] ActionBeanContext}, or [EMAIL PROTECTED] ActionBean}
associated with the request yet.</li>
+ * <li>None of the request parameters in the POST body can be read without
risking denial of
+ * service. That includes the [EMAIL PROTECTED] _sourcePage} parameter
that indicates the page from which
+ * the request was submitted.</li>
+ * </ul>
+ * </p>
+ * <p>
+ * This exception handler makes an attempt to handle the exception as
gracefully as possible. It
+ * relies on the HTTP Referer header to determine where the request was
submitted from. It uses
+ * introspection to guess the field name of the [EMAIL PROTECTED]
FileBean} field that exceeded the POST
+ * limit. It instantiates an [EMAIL PROTECTED] ActionBean} and [EMAIL
PROTECTED] ActionBeanContext} and adds a
+ * validation error to report the field name, maximum POST size, and
actual POST size. Finally,
+ * it forwards to the referer.
+ * </p>
+ * <p>
+ * While this is a best effort, it won't be ideal for all situations.
Developers can extend this
+ * class and override this method, delegating to the superclass when
necessary. If this method
+ * is unable to handle the exception properly for any reason, it returns
null.
+ * </p>
+ *
+ * @param exception The exception that needs to be handled
+ * @param request The servlet request
+ * @param response The servlet response
+ * @return Usually, a [EMAIL PROTECTED] Resolution} to forward to the page
from which this request was
+ * submitted. If, for some reason, that can't be accomplished then
it returns null.
+ * @throws ServletException
+ */
+ protected Resolution handle(FileUploadLimitExceededException exception,
+ HttpServletRequest request, HttpServletResponse response) throws
ServletException {
+ // Get the path to which we will forward to display the message
+ final String path = getFileUploadExceededExceptionPath(request);
+ if (path == null)
+ return null;
+
+ // Create the ActionBean and ActionBeanContext
+ final ActionBeanContext context;
+ final ActionBean actionBean;
+ try {
+ context =
configuration.getActionBeanContextFactory().getContextInstance(request,
+ response);
+ actionBean =
configuration.getActionResolver().getActionBean(context);
+ request.setAttribute(StripesConstants.REQ_ATTR_ACTION_BEAN,
actionBean);
+ }
+ catch (ServletException e) {
+ log.error(e);
+ return null;
+ }
+
+ // Try to guess the field name by finding exactly one FileBean field
+ String fieldName = null;
+ try {
+ BeanInfo beanInfo =
Introspector.getBeanInfo(actionBean.getClass());
+ for (PropertyDescriptor pd : beanInfo.getPropertyDescriptors()) {
+ if (FileBean.class.isAssignableFrom(pd.getPropertyType())) {
+ if (fieldName == null) {
+ // First FileBean field found so set the field name
+ fieldName = pd.getName();
+ }
+ else {
+ // There's more than one FileBean field so don't use a
field name
+ fieldName = null;
+ break;
+ }
+ }
+ }
+ }
+ catch (Exception e) {
+ // Not a big deal if we can't determine the field name
+ }
+
+ // Add validation error with parameters for max post size and actual
posted size (KB)
+ DecimalFormat format = new DecimalFormat("0.00");
+ double max = (double) exception.getMaximum() / 1024;
+ double posted = (double) exception.getPosted() / 1024;
+ LocalizableError error = new
LocalizableError("validation.file.postBodyTooBig", format
+ .format(max), format.format(posted));
+ if (fieldName == null)
+ context.getValidationErrors().addGlobalError(error);
+ else
+ context.getValidationErrors().add(fieldName, error);
+
+ // Create an ExecutionContext so that the validation errors can be
filled in
+ ExecutionContext exectx = new ExecutionContext();
+ exectx.setActionBean(actionBean);
+ exectx.setActionBeanContext(context);
+ DispatcherHelper.fillInValidationErrors(exectx);
+
+ // Redirect back to referer
+ return new RedirectResolution(path) {
+ @Override
+ public void execute(HttpServletRequest request,
HttpServletResponse response)
+ throws ServletException, IOException {
+ // Create a new request wrapper, avoiding the pitfalls of
multipart
+ StripesRequestWrapper wrapper = new
StripesRequestWrapper(request) {
+ @Override
+ protected void
constructMultipartWrapper(HttpServletRequest request)
+ throws StripesServletException {
+
setLocale(configuration.getLocalePicker().pickLocale(request));
+ }
+ };
+
+ // Pass wrapper to superclass
+ super.execute(wrapper, response);
+ }
+ }.flash(actionBean);
+ }
+
+ /**
+ * Get the path to which the [EMAIL PROTECTED] Resolution} returned by
+ * [EMAIL PROTECTED] #handle(FileUploadLimitExceededException,
HttpServletRequest, HttpServletResponse)}
+ * should forward to report the error. The default implementation attempts
to determine this
+ * from the HTTP Referer header. If it is unable to do so, it returns
null. Subclasses may
+ * override this method to return whatever they wish. The return value
must be relative to the
+ * application context root.
+ *
+ * @param request The request that generated the exception
+ * @return The context-relative path from which the request was submitted
+ */
+ protected String getFileUploadExceededExceptionPath(HttpServletRequest
request) {
+ // Get the referer URL so we can bounce back to it
+ URL referer = null;
+ try {
+ referer = new URL(request.getHeader("referer"));
+ }
+ catch (Exception e) {
+ // Header not found? Invalid? Can't do anything with it :(
+ return null;
+ }
+
+ // Convert the referer path to a context-relative path
+ String path = referer.getFile();
+ String contextPath = request.getContextPath();
+ if (contextPath.length() > 0) {
+ // We can't handle it if the POST came from outside our app
+ if (!path.startsWith(contextPath + "/"))
+ return null;
+
+ path = path.replace(contextPath, "");
+ }
+
+ return path;
+ }
+
/** Stores the configuration and examines the handler for usable delegate
methods. */
public void init(Configuration configuration) throws Exception {
this.configuration = configuration;
This was sent by the SourceForge.net collaborative development platform, the
world's largest Open Source development site.
-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft
Defy all challenges. Microsoft(R) Visual Studio 2008.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
_______________________________________________
Stripes-development mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/stripes-development