user friendly error pages for codes 404, 403 and 500

Sun, 31 Dec 2017 17:43:39 +0100

author
Mike Becker <universe@uap-core.de>
date
Sun, 31 Dec 2017 17:43:39 +0100
changeset 18
a94b172c3a93
parent 17
d1036b776eee
child 19
1a0ac419f714

user friendly error pages for codes 404, 403 and 500

src/java/de/uapcore/lightpit/AbstractLightPITServlet.java file | annotate | diff | comparison | revisions
src/java/de/uapcore/lightpit/LightPITModule.java file | annotate | diff | comparison | revisions
src/java/de/uapcore/lightpit/ModuleManager.java file | annotate | diff | comparison | revisions
src/java/de/uapcore/lightpit/RequestMapping.java file | annotate | diff | comparison | revisions
src/java/de/uapcore/lightpit/modules/ErrorModule.java file | annotate | diff | comparison | revisions
src/java/de/uapcore/lightpit/resources/localization/error.properties file | annotate | diff | comparison | revisions
src/java/de/uapcore/lightpit/resources/localization/error_de.properties file | annotate | diff | comparison | revisions
web/WEB-INF/dynamic_fragments/error.jsp file | annotate | diff | comparison | revisions
web/WEB-INF/web.xml file | annotate | diff | comparison | revisions
web/error.css file | annotate | diff | comparison | revisions
web/lightpit.css file | annotate | diff | comparison | revisions
--- a/src/java/de/uapcore/lightpit/AbstractLightPITServlet.java	Sat Dec 30 20:41:55 2017 +0100
+++ b/src/java/de/uapcore/lightpit/AbstractLightPITServlet.java	Sun Dec 31 17:43:39 2017 +0100
@@ -73,6 +73,12 @@
     
     /**
      * Invocation mapping gathered from the {@link RequestMapping} annotations.
+     * 
+     * Paths in this map must always start with a leading slash, although
+     * the specification in the annotation must not start with a leading slash.
+     * 
+     * The reason for this is the different handling of empty paths in 
+     * {@link HttpServletRequest#getPathInfo()}.
      */
     private final Map<HttpMethod, Map<String, HandlerMethod>> mappings = new HashMap<>();
 
@@ -145,9 +151,11 @@
                     if (params.length == 2
                             && HttpServletRequest.class.isAssignableFrom(params[0])
                             && HttpServletResponse.class.isAssignableFrom(params[1])) {
+                        
+                        final String requestPath = "/"+mapping.get().requestPath();
 
                         if (mappings.computeIfAbsent(mapping.get().method(), k -> new HashMap<>()).
-                                putIfAbsent(mapping.get().requestPath(),
+                                putIfAbsent(requestPath,
                                         (req, resp) -> invokeMapping(method, req, resp)) != null) {
                             LOG.warn("{} {} has multiple mappings",
                                     mapping.get().method(),
@@ -157,7 +165,7 @@
 
                         LOG.info("{} {} maps to {}",
                                 mapping.get().method(),
-                                mapping.get().requestPath(),
+                                requestPath,
                                 method.getName()
                         );
                     } else {
@@ -219,7 +227,7 @@
     
     private Optional<HandlerMethod> findMapping(HttpMethod method, HttpServletRequest req) {
         return Optional.ofNullable(mappings.get(method)).map(
-                (rm) -> rm.get(Optional.ofNullable(req.getPathInfo()).orElse(""))
+                (rm) -> rm.get(Optional.ofNullable(req.getPathInfo()).orElse("/"))
         );
     }
     
--- a/src/java/de/uapcore/lightpit/LightPITModule.java	Sat Dec 30 20:41:55 2017 +0100
+++ b/src/java/de/uapcore/lightpit/LightPITModule.java	Sun Dec 31 17:43:39 2017 +0100
@@ -71,6 +71,10 @@
     
     /**
      * Returns the properties key for the menu label.
+     * 
+     * Set this string to empty string, if the module should be hidden from
+     * the menu.
+     * 
      * @return the properties key
      */
     String menuKey() default "menuLabel";
--- a/src/java/de/uapcore/lightpit/ModuleManager.java	Sat Dec 30 20:41:55 2017 +0100
+++ b/src/java/de/uapcore/lightpit/ModuleManager.java	Sun Dec 31 17:43:39 2017 +0100
@@ -31,6 +31,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.Objects;
 import java.util.Optional;
 import javax.servlet.Registration;
 import javax.servlet.ServletContext;
@@ -118,10 +119,12 @@
     
     private void handleServletRegistration(String name, Registration reg) {
         final Optional<LightPITModule> moduleInfo = getModuleInfo(reg);
-        if (moduleInfo.isPresent()) {
+        if (moduleInfo.isPresent()) {            
+            // TODO: implement dependency resolver
             
-            // TODO: remove this call and add the module to some dependency resolver, first
-            addModuleToMenu(reg.getClassName(), moduleInfo.get());
+            if (!moduleInfo.get().menuKey().isEmpty()) {
+                addModuleToMenu(reg.getClassName(), moduleInfo.get());
+            }
             
             LOG.info("Module detected: {}", name);
         } else {
--- a/src/java/de/uapcore/lightpit/RequestMapping.java	Sat Dec 30 20:41:55 2017 +0100
+++ b/src/java/de/uapcore/lightpit/RequestMapping.java	Sun Dec 31 17:43:39 2017 +0100
@@ -59,7 +59,7 @@
      * If a menu key is specified, this is also the path, which is linked
      * by the menu entry.
      * 
-     * The path must be specified <em>without</em> a trailing slash.
+     * The path must be specified <em>without</em> leading and trailing slash.
      * 
      * @return the request path the annotated method should handle
      */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java/de/uapcore/lightpit/modules/ErrorModule.java	Sun Dec 31 17:43:39 2017 +0100
@@ -0,0 +1,80 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ * 
+ * Copyright 2017 Mike Becker. All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ * 
+ */
+package de.uapcore.lightpit.modules;
+
+import de.uapcore.lightpit.LightPITModule;
+import de.uapcore.lightpit.AbstractLightPITServlet;
+import de.uapcore.lightpit.HttpMethod;
+import de.uapcore.lightpit.RequestMapping;
+import de.uapcore.lightpit.ResponseType;
+import javax.servlet.annotation.WebServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * Entry point for the application.
+ */
+@LightPITModule(
+        bundleBaseName = "de.uapcore.lightpit.resources.localization.error",
+        modulePath = "error",
+        menuKey = "",
+        titleKey = "title"
+)
+@WebServlet(
+        name = "ErrorModule",
+        urlPatterns = "/error/*"
+)
+public final class ErrorModule extends AbstractLightPITServlet {
+    
+    public static final String REQ_ATTR_ERROR_CODE = "errorCode";
+    
+    private ResponseType handle(HttpServletRequest req, HttpServletResponse resp, int sc) {
+        
+        req.setAttribute(REQ_ATTR_ERROR_CODE, sc);
+        setStylesheet(req, "error");
+        setDynamicFragment(req, "error");
+        
+        return ResponseType.HTML_FULL;
+    }
+    
+    @RequestMapping(requestPath = "404", method = HttpMethod.GET)
+    public ResponseType handle404(HttpServletRequest req, HttpServletResponse resp) {
+        return handle(req, resp, 404);
+    }
+    
+    @RequestMapping(requestPath = "403", method = HttpMethod.GET)
+    public ResponseType handle403(HttpServletRequest req, HttpServletResponse resp) {
+        return handle(req, resp, 403);
+    }
+    
+    @RequestMapping(requestPath = "500", method = HttpMethod.GET)
+    public ResponseType handle500(HttpServletRequest req, HttpServletResponse resp) {
+        return handle(req, resp, 500);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java/de/uapcore/lightpit/resources/localization/error.properties	Sun Dec 31 17:43:39 2017 +0100
@@ -0,0 +1,35 @@
+# Copyright 2017 Mike Becker. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+
+title = Error
+
+h1 = The requested page cannot be displayed.
+errorCode = Code
+
+errorMessage = Message
+errorMessage.404 = Page not found
+errorMessage.403 = Access denied
+errorMessage.500 = Internal error
+
+errorTimestamp = Timestamp
+errorExceptionText = Internal Exception
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java/de/uapcore/lightpit/resources/localization/error_de.properties	Sun Dec 31 17:43:39 2017 +0100
@@ -0,0 +1,35 @@
+# Copyright 2017 Mike Becker. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+
+title = Fehler
+
+h1 = Die angeforderte Seite kann nicht angezeigt werden.
+errorCode = Fehlercode
+
+errorMessage = Nachricht
+errorMessage.404 = Seite nicht gefunden
+errorMessage.403 = Zugriff verboten
+errorMessage.500 = Interner Fehler
+
+errorTimestamp = Zeitstempel
+errorExceptionText = Interne Ausnahme
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web/WEB-INF/dynamic_fragments/error.jsp	Sun Dec 31 17:43:39 2017 +0100
@@ -0,0 +1,56 @@
+<%-- 
+DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+Copyright 2017 Mike Becker. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+--%>
+<%@page pageEncoding="UTF-8" session="true" %>
+<%@page import="de.uapcore.lightpit.modules.ErrorModule" %>
+<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
+<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
+
+<c:set scope="page" var="errorCode" value="${requestScope[ErrorModule.REQ_ATTR_ERROR_CODE]}"/>
+
+<div id="error-page">
+    <h1><fmt:message key="h1"/></h1>
+    <table>
+        <tr>
+            <th><fmt:message key="errorCode" />:</th>
+            <td>${errorCode}</td>
+        </tr>
+        <tr>
+            <th><fmt:message key="errorMessage" />:</th>
+            <td><fmt:message key="errorMessage.${errorCode}" /></td>
+        </tr>
+        <tr>
+            <th><fmt:message key="errorTimestamp" />:</th>
+            <td><fmt:formatDate type="both" value="<%= new java.util.Date()%>"/></td>
+        </tr>
+        <c:if test="${not empty exception}">
+        <tr>
+            <th><fmt:message key="errorExceptionText" />:</th>
+            <td>${exception.class.name} - ${exception.message}</td>
+        </tr>    
+        </c:if>
+    </table>
+</div>
--- a/web/WEB-INF/web.xml	Sat Dec 30 20:41:55 2017 +0100
+++ b/web/WEB-INF/web.xml	Sun Dec 31 17:43:39 2017 +0100
@@ -9,4 +9,16 @@
         <param-name>available-languages</param-name>
         <param-value>en,de</param-value>
     </context-param>
+    <error-page>
+        <error-code>404</error-code>
+        <location>/error/404</location>
+    </error-page>
+    <error-page>
+        <error-code>403</error-code>
+        <location>/error/403</location>
+    </error-page>
+    <error-page>
+        <error-code>500</error-code>
+        <location>/error/500</location>
+    </error-page>
 </web-app>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web/error.css	Sun Dec 31 17:43:39 2017 +0100
@@ -0,0 +1,55 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ * 
+ * Copyright 2017 Mike Becker. All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ * 
+ */
+
+#error-page h1 {
+    font-size: 1.5em;
+}
+
+#error-page table {
+    width: 100%;
+    
+    border-top-style: solid;
+    border-top-width: 1pt;
+    border-top-color: #606060;
+    
+    border-bottom-style: solid;
+    border-bottom-width: 1pt;
+    border-bottom-color: #505050;
+    
+    border-collapse: separate;
+    border-spacing: .5em;
+}
+
+#error-page table th {
+    text-align: right;
+}
+
+#error-page table td {
+    width: 100%;
+}
--- a/web/lightpit.css	Sat Dec 30 20:41:55 2017 +0100
+++ b/web/lightpit.css	Sun Dec 31 17:43:39 2017 +0100
@@ -39,7 +39,7 @@
     border-style: solid;
     border-width: 1pt;
     
-    color: black;
+    color: #1c202e;
 }
 
 a {

mercurial