From ea75fadc9670d12ee4c6523a7d3d03ec1c5ead54 Mon Sep 17 00:00:00 2001 From: Lukasz Lenart Date: Sun, 16 Feb 2014 18:36:45 +0100 Subject: [PATCH] WW-3887 Extracts dedicated interface (with default implementation) to handle errors --- .../org/apache/struts2/StrutsConstants.java | 2 + .../DefaultDispatcherErrorHandler.java | 110 ++++++++++++++++ .../apache/struts2/dispatcher/Dispatcher.java | 121 +++++------------- .../dispatcher/DispatcherErrorHandler.java | 29 +++++ core/src/main/resources/struts-default.xml | 4 + 5 files changed, 179 insertions(+), 87 deletions(-) create mode 100644 core/src/main/java/org/apache/struts2/dispatcher/DefaultDispatcherErrorHandler.java create mode 100644 core/src/main/java/org/apache/struts2/dispatcher/DispatcherErrorHandler.java diff --git a/core/src/main/java/org/apache/struts2/StrutsConstants.java b/core/src/main/java/org/apache/struts2/StrutsConstants.java index 1cb4ab578..3423ec8bd 100644 --- a/core/src/main/java/org/apache/struts2/StrutsConstants.java +++ b/core/src/main/java/org/apache/struts2/StrutsConstants.java @@ -279,4 +279,6 @@ public final class StrutsConstants { public static final String DEFAULT_TEMPLATE_TYPE_CONFIG_KEY = "struts.ui.templateSuffix"; + /** Allows override default DispatcherErrorHandler **/ + public static final String STRUTS_DISPATCHER_ERROR_HANDLER = "struts.dispatcher.errorHandler"; } diff --git a/core/src/main/java/org/apache/struts2/dispatcher/DefaultDispatcherErrorHandler.java b/core/src/main/java/org/apache/struts2/dispatcher/DefaultDispatcherErrorHandler.java new file mode 100644 index 000000000..b09c890a1 --- /dev/null +++ b/core/src/main/java/org/apache/struts2/dispatcher/DefaultDispatcherErrorHandler.java @@ -0,0 +1,110 @@ +package org.apache.struts2.dispatcher; + +import com.opensymphony.xwork2.inject.Inject; +import com.opensymphony.xwork2.util.location.Location; +import com.opensymphony.xwork2.util.location.LocationUtils; +import com.opensymphony.xwork2.util.logging.Logger; +import com.opensymphony.xwork2.util.logging.LoggerFactory; +import freemarker.template.Template; +import org.apache.struts2.StrutsConstants; +import org.apache.struts2.StrutsException; +import org.apache.struts2.views.freemarker.FreemarkerManager; + +import javax.servlet.ServletContext; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.StringWriter; +import java.io.Writer; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +public class DefaultDispatcherErrorHandler implements DispatcherErrorHandler { + + private static final Logger LOG = LoggerFactory.getLogger(DefaultDispatcherErrorHandler.class); + + private FreemarkerManager freemarkerManager; + private boolean devMode; + private Template template; + + @Inject + public void setFreemarkerManager(FreemarkerManager freemarkerManager) { + this.freemarkerManager = freemarkerManager; + } + + @Inject(StrutsConstants.STRUTS_DEVMODE) + public void setDevMode(String devMode) { + this.devMode = "true".equalsIgnoreCase(devMode); + } + + public void init(ServletContext ctx) { + try { + freemarker.template.Configuration config = freemarkerManager.getConfiguration(ctx); + template = config.getTemplate("/org/apache/struts2/dispatcher/error.ftl"); + } catch (IOException e) { + throw new StrutsException(e); + } + } + + public void handleError(HttpServletRequest request, HttpServletResponse response, int code, Exception e) { + Boolean devModeOverride = FilterDispatcher.getDevModeOverride(); + if (devModeOverride != null ? devModeOverride : devMode) { + if (LOG.isDebugEnabled()) { + LOG.debug("Exception occurred during processing request: #0", e, e.getMessage()); + } + try { + + List chain = new ArrayList(); + Throwable cur = e; + chain.add(cur); + while ((cur = cur.getCause()) != null) { + chain.add(cur); + } + + HashMap data = new HashMap(); + data.put("exception", e); + data.put("unknown", Location.UNKNOWN); + data.put("chain", chain); + data.put("locator", new Dispatcher.Locator()); + + Writer writer = new StringWriter(); + template.process(data, writer); + + response.setContentType("text/html"); + response.getWriter().write(writer.toString()); + response.getWriter().close(); + } catch (Exception exp) { + try { + if (LOG.isDebugEnabled()) { + LOG.debug("Cannot show problem report!", exp); + } + response.sendError(code, "Unable to show problem report:\n" + exp + "\n\n" + LocationUtils.getLocation(exp)); + } catch (IOException ex) { + // we're already sending an error, not much else we can do if more stuff breaks + } + } + } else { + try { + // WW-1977: Only put errors in the request when code is a 500 error + if (code == HttpServletResponse.SC_INTERNAL_SERVER_ERROR) { + // WW-4103: Only logs error when application error occurred, not Struts error + if (LOG.isErrorEnabled()) { + LOG.error("Exception occurred during processing request: #0", e, e.getMessage()); + } + // send a http error response to use the servlet defined error handler + // make the exception availible to the web.xml defined error page + request.setAttribute("javax.servlet.error.exception", e); + + // for compatibility + request.setAttribute("javax.servlet.jsp.jspException", e); + } + + // send the error response + response.sendError(code, e.getMessage()); + } catch (IOException e1) { + // we're already sending an error, not much else we can do if more stuff breaks + } + } + } +} diff --git a/core/src/main/java/org/apache/struts2/dispatcher/Dispatcher.java b/core/src/main/java/org/apache/struts2/dispatcher/Dispatcher.java index 9c490ac32..36d858d6a 100644 --- a/core/src/main/java/org/apache/struts2/dispatcher/Dispatcher.java +++ b/core/src/main/java/org/apache/struts2/dispatcher/Dispatcher.java @@ -21,20 +21,8 @@ package org.apache.struts2.dispatcher; -import com.opensymphony.xwork2.ActionContext; -import com.opensymphony.xwork2.ActionProxy; -import com.opensymphony.xwork2.ActionProxyFactory; -import com.opensymphony.xwork2.FileManager; -import com.opensymphony.xwork2.FileManagerFactory; -import com.opensymphony.xwork2.LocaleProvider; -import com.opensymphony.xwork2.ObjectFactory; -import com.opensymphony.xwork2.Result; -import com.opensymphony.xwork2.config.Configuration; -import com.opensymphony.xwork2.config.ConfigurationException; -import com.opensymphony.xwork2.config.ConfigurationManager; -import com.opensymphony.xwork2.config.ConfigurationProvider; -import com.opensymphony.xwork2.config.FileManagerFactoryProvider; -import com.opensymphony.xwork2.config.FileManagerProvider; +import com.opensymphony.xwork2.*; +import com.opensymphony.xwork2.config.*; import com.opensymphony.xwork2.config.entities.InterceptorMapping; import com.opensymphony.xwork2.config.entities.InterceptorStackConfig; import com.opensymphony.xwork2.config.entities.PackageConfig; @@ -53,7 +41,6 @@ import com.opensymphony.xwork2.util.location.LocationUtils; import com.opensymphony.xwork2.util.logging.Logger; import com.opensymphony.xwork2.util.logging.LoggerFactory; import com.opensymphony.xwork2.util.profiling.UtilTimerStack; -import freemarker.template.Template; import org.apache.struts2.ServletActionContext; import org.apache.struts2.StrutsConstants; import org.apache.struts2.StrutsException; @@ -68,7 +55,6 @@ import org.apache.struts2.dispatcher.multipart.MultiPartRequestWrapper; import org.apache.struts2.util.AttributeMap; import org.apache.struts2.util.ObjectFactoryDestroyable; import org.apache.struts2.util.fs.JBossFileManager; -import org.apache.struts2.views.freemarker.FreemarkerManager; import javax.servlet.ServletContext; import javax.servlet.ServletException; @@ -76,16 +62,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.File; import java.io.IOException; -import java.io.StringWriter; -import java.io.Writer; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Set; +import java.util.*; import java.util.concurrent.CopyOnWriteArrayList; /** @@ -168,6 +145,11 @@ public class Dispatcher { */ private boolean handleException; + /** + * Interface used to handle internal errors or missing resources + */ + private DispatcherErrorHandler errorHandler; + /** * Provide the dispatcher instance for the current thread. * @@ -280,6 +262,11 @@ public class Dispatcher { this.handleException = Boolean.parseBoolean(handleException); } + @Inject + public void setDispatcherErrorHandler(DispatcherErrorHandler errorHandler) { + this.errorHandler = errorHandler; + } + /** * Releases all instances bound to this dispatcher instance. */ @@ -492,6 +479,10 @@ public class Dispatcher { l.dispatcherInitialized(this); } } + //if (servletContext != null) { + errorHandler.init(servletContext); + //} + } catch (Exception ex) { if (LOG.isErrorEnabled()) LOG.error("Dispatcher initialization failed", ex); @@ -847,70 +838,26 @@ public class Dispatcher { * @param code the HttpServletResponse error code (see {@link javax.servlet.http.HttpServletResponse} for possible error codes). * @param e the Exception that is reported. * @param ctx the ServletContext object. + * + * @deprecated remove in version 3.0 - use version without ServletContext parameter */ + @Deprecated public void sendError(HttpServletRequest request, HttpServletResponse response, ServletContext ctx, int code, Exception e) { - Boolean devModeOverride = FilterDispatcher.getDevModeOverride(); - if (devModeOverride != null ? devModeOverride : devMode) { - if (LOG.isDebugEnabled()) { - LOG.debug("Exception occurred during processing request: #0", e, e.getMessage()); - } - try { - FreemarkerManager mgr = getContainer().getInstance(FreemarkerManager.class); + sendError(request, response, code, e); + } - freemarker.template.Configuration config = mgr.getConfiguration(ctx); - Template template = config.getTemplate("/org/apache/struts2/dispatcher/error.ftl"); - - List chain = new ArrayList(); - Throwable cur = e; - chain.add(cur); - while ((cur = cur.getCause()) != null) { - chain.add(cur); - } - - HashMap data = new HashMap(); - data.put("exception", e); - data.put("unknown", Location.UNKNOWN); - data.put("chain", chain); - data.put("locator", new Locator()); - - Writer writer = new StringWriter(); - template.process(data, writer); - - response.setContentType("text/html"); - response.getWriter().write(writer.toString()); - response.getWriter().close(); - } catch (Exception exp) { - try { - if (LOG.isDebugEnabled()) { - LOG.debug("Cannot show problem report!", exp); - } - response.sendError(code, "Unable to show problem report:\n" + exp + "\n\n" + LocationUtils.getLocation(exp)); - } catch (IOException ex) { - // we're already sending an error, not much else we can do if more stuff breaks - } - } - } else { - try { - // WW-1977: Only put errors in the request when code is a 500 error - if (code == HttpServletResponse.SC_INTERNAL_SERVER_ERROR) { - // WW-4103: Only logs error when application error occurred, not Struts error - if (LOG.isErrorEnabled()) { - LOG.error("Exception occurred during processing request: #0", e, e.getMessage()); - } - // send a http error response to use the servlet defined error handler - // make the exception availible to the web.xml defined error page - request.setAttribute("javax.servlet.error.exception", e); - - // for compatibility - request.setAttribute("javax.servlet.jsp.jspException", e); - } - - // send the error response - response.sendError(code, e.getMessage()); - } catch (IOException e1) { - // we're already sending an error, not much else we can do if more stuff breaks - } - } + /** + * Send an HTTP error response code. + * + * @param request the HttpServletRequest object. + * @param response the HttpServletResponse object. + * @param code the HttpServletResponse error code (see {@link javax.servlet.http.HttpServletResponse} for possible error codes). + * @param e the Exception that is reported. + * + * @since 2.3.17 + */ + public void sendError(HttpServletRequest request, HttpServletResponse response, int code, Exception e) { + errorHandler.handleError(request, response, code, e); } /** diff --git a/core/src/main/java/org/apache/struts2/dispatcher/DispatcherErrorHandler.java b/core/src/main/java/org/apache/struts2/dispatcher/DispatcherErrorHandler.java new file mode 100644 index 000000000..fe8f6ecc7 --- /dev/null +++ b/core/src/main/java/org/apache/struts2/dispatcher/DispatcherErrorHandler.java @@ -0,0 +1,29 @@ +package org.apache.struts2.dispatcher; + +import javax.servlet.ServletContext; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * Implementation of this interface is used to handle internal errors or missing resources. + * Basically it sends back HTTP error codes or error page depends on requirements. + */ +public interface DispatcherErrorHandler { + + /** + * Init instance after creating {@link org.apache.struts2.dispatcher.Dispatcher} + * @param ctx current {@link javax.servlet.ServletContext} + */ + public void init(ServletContext ctx); + + /** + * Handle passed error code or exception + * + * @param request current {@link javax.servlet.http.HttpServletRequest} + * @param response current {@link javax.servlet.http.HttpServletResponse} + * @param code HTTP Error Code, see {@link javax.servlet.http.HttpServletResponse} for possible error codes + * @param e Exception to report + */ + public void handleError(HttpServletRequest request, HttpServletResponse response, int code, Exception e); + +} diff --git a/core/src/main/resources/struts-default.xml b/core/src/main/resources/struts-default.xml index 635b241cb..c65963af8 100644 --- a/core/src/main/resources/struts-default.xml +++ b/core/src/main/resources/struts-default.xml @@ -132,6 +132,10 @@ + + + +