From 22782bd4b8a0a6698aedc80d48c1e9b166ac0b8d Mon Sep 17 00:00:00 2001 From: Olivier Maury <Olivier.Maury@inrae.fr> Date: Mon, 22 Apr 2024 17:12:09 +0200 Subject: [PATCH 1/2] Personnaliser les pages d'erreur. fixes #39 --- pom.xml | 2 +- www-server/pom.xml | 55 ++++++----- .../www/server/ErrorHandlerServlet.java | 93 +++++++++++++++++++ .../fr/agrometinfo/www/server/i18n.properties | 5 + .../agrometinfo/www/server/i18n_fr.properties | 5 + .../fr/agrometinfo/www/server/jsp.properties | 9 ++ .../agrometinfo/www/server/jsp_fr.properties | 9 ++ www-server/src/main/webapp/WEB-INF/error.jsp | 12 +++ .../src/main/webapp/WEB-INF/tags/page.tag | 84 +++++++++++++++++ www-server/src/main/webapp/WEB-INF/web.xml | 3 + 10 files changed, 254 insertions(+), 23 deletions(-) create mode 100644 www-server/src/main/java/fr/agrometinfo/www/server/ErrorHandlerServlet.java create mode 100644 www-server/src/main/resources/fr/agrometinfo/www/server/jsp.properties create mode 100644 www-server/src/main/resources/fr/agrometinfo/www/server/jsp_fr.properties create mode 100644 www-server/src/main/webapp/WEB-INF/error.jsp create mode 100644 www-server/src/main/webapp/WEB-INF/tags/page.tag diff --git a/pom.xml b/pom.xml index 79ab203..1ef5fe5 100644 --- a/pom.xml +++ b/pom.xml @@ -91,7 +91,7 @@ <artifactId>junit-jupiter-params</artifactId> <version>${junit.version}</version> <scope>test</scope> - </dependency> + </dependency> </dependencies> <dependencyManagement> diff --git a/www-server/pom.xml b/www-server/pom.xml index 5f69e6e..a98d207 100644 --- a/www-server/pom.xml +++ b/www-server/pom.xml @@ -125,14 +125,14 @@ </dependency> <!-- Jakarta mail --> <dependency> - <groupId>jakarta.mail</groupId> - <artifactId>jakarta.mail-api</artifactId> - <version>2.1.3</version> + <groupId>jakarta.mail</groupId> + <artifactId>jakarta.mail-api</artifactId> + <version>2.1.3</version> </dependency> <dependency> - <groupId>org.eclipse.angus</groupId> - <artifactId>jakarta.mail</artifactId> - <version>2.0.3</version> + <groupId>org.eclipse.angus</groupId> + <artifactId>jakarta.mail</artifactId> + <version>2.0.3</version> </dependency> <!-- JPA --> <dependency> @@ -155,17 +155,28 @@ <artifactId>postgresql</artifactId> <version>42.7.3</version> </dependency> + <!-- JSTL --> + <dependency> + <groupId>jakarta.servlet.jsp.jstl</groupId> + <artifactId>jakarta.servlet.jsp.jstl-api</artifactId> + <version>3.0.0</version> + </dependency> + <dependency> + <groupId>org.glassfish.web</groupId> + <artifactId>jakarta.servlet.jsp.jstl</artifactId> + <version>3.0.0</version> + </dependency> <!-- fast-serialization --> <!-- https://mvnrepository.com/artifact/de.ruedigermoeller/fst --> <dependency> - <groupId>com.fasterxml.jackson.core</groupId> - <artifactId>jackson-core</artifactId> - <version>2.16.1</version> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-core</artifactId> + <version>2.16.1</version> </dependency> <dependency> - <groupId>de.ruedigermoeller</groupId> - <artifactId>fst</artifactId> - <version>3.0.4-jdk17</version> + <groupId>de.ruedigermoeller</groupId> + <artifactId>fst</artifactId> + <version>3.0.4-jdk17</version> </dependency> <!-- SAVA --> <dependency> @@ -182,20 +193,20 @@ </dependency> <!-- Tests for Jersey --> <dependency> - <groupId>org.glassfish.jersey.test-framework</groupId> - <artifactId>jersey-test-framework-core</artifactId> - <scope>test</scope> + <groupId>org.glassfish.jersey.test-framework</groupId> + <artifactId>jersey-test-framework-core</artifactId> + <scope>test</scope> </dependency> <dependency> - <groupId>org.glassfish.jersey.test-framework.providers</groupId> - <artifactId>jersey-test-framework-provider-grizzly2</artifactId> - <scope>test</scope> + <groupId>org.glassfish.jersey.test-framework.providers</groupId> + <artifactId>jersey-test-framework-provider-grizzly2</artifactId> + <scope>test</scope> </dependency> <dependency> - <groupId>org.mockito</groupId> - <artifactId>mockito-core</artifactId> - <version>${mockito-core.version}</version> - <scope>test</scope> + <groupId>org.mockito</groupId> + <artifactId>mockito-core</artifactId> + <version>${mockito-core.version}</version> + <scope>test</scope> </dependency> </dependencies> diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/ErrorHandlerServlet.java b/www-server/src/main/java/fr/agrometinfo/www/server/ErrorHandlerServlet.java new file mode 100644 index 0000000..90a9e3b --- /dev/null +++ b/www-server/src/main/java/fr/agrometinfo/www/server/ErrorHandlerServlet.java @@ -0,0 +1,93 @@ +package fr.agrometinfo.www.server; + +import java.io.IOException; +import java.util.Locale; + +import fr.agrometinfo.www.server.AgroMetInfoConfiguration.ConfigurationKey; +import fr.agrometinfo.www.server.util.LocaleUtils; +import jakarta.inject.Inject; +import jakarta.servlet.RequestDispatcher; +import jakarta.servlet.ServletException; +import jakarta.servlet.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +/** + * Servlet to display user friendly error messages. + * + * @author Olivier Maury + */ +@WebServlet(urlPatterns = "/errorHandler") +public final class ErrorHandlerServlet extends HttpServlet { + /** + * UID. + */ + private static final long serialVersionUID = 1003353995341179825L; + + /** + * Application config. + */ + @Inject + private AgroMetInfoConfiguration config; + + @Override + protected void doGet(final HttpServletRequest request, final HttpServletResponse response) + throws IOException, ServletException { + final String appUrl = config.get(ConfigurationKey.APP_URL) + "../"; + final Exception exception = (Exception) request.getAttribute(RequestDispatcher.ERROR_EXCEPTION); + + final Locale locale = LocaleUtils.getLocale(request); + final I18n i18n = new I18n("fr.agrometinfo.www.server.i18n", locale); + String body; + final String title = i18n.format(response.getStatus(), "http.status.title"); + switch (response.getStatus()) { + case HttpServletResponse.SC_BAD_REQUEST: + body = "<p>La demande n'a pas pu être comprise par le serveur à cause d'une syntaxe mal formée.<br />" + + "Vous NE DEVRIEZ PAS répéter la demande sans modifications.</p>"; + break; + case HttpServletResponse.SC_NOT_FOUND: + body = "<p>La page que vous cherchez n'existe pas, mais vous n'êtes pas " + + "pour autant dans une impasse. Voici quelques options disponibles :</p>" // + + "<ul>" + + "<li>Assurez-vous d'utiliser la bonne adresse URL.</li>" + "<li>Consultez <a href=\"" + appUrl + + "\" target=\"_blank\">la documentation</a>.</li>" + + "<li>Obtenez de l'aide en écrivant une demande de support dans l'application ou <a href=\"" + + appUrl + "/contact.html\">contactez-nous</a>.</li>" // + + "</ul>"; + break; + case HttpServletResponse.SC_INTERNAL_SERVER_ERROR: + body = "<p>Le serveur a rencontré une condition inattendue qui l'empêche de satisfaire la demande.</p>" + + "<p><b>Erreur :</b> " + exception + "</p>" + "<p><b>Cause :</b> " + exception.getCause() + + "</p>"; + break; + case HttpServletResponse.SC_SERVICE_UNAVAILABLE: + body = "<p>Le serveur est incapable de traiter actuellement la demande à cause d'une surcharge temporaire " + + "ou de la maintenance du serveur.<br />" + + "Il s'agit d'une condition temporaire qui sera levée après un certain temps.</p>"; + break; + default: + body = "<p><b>Erreur :</b> " + exception + "</p>"; + if (exception != null) { + body += "<p><b>Message d'erreur :</b>" + exception.getMessage() + "</p>"; + } + body += "<p><b>Status code:</b> " + response.getStatus() + "</p>"; + body += "<p><b>Request URI:</b> " + request.getScheme() + "://" + request.getServerName() + + request.getRequestURI() + "</p>"; + body += "<p><b>URL:</b> " + request.getRequestURI() + "</p>"; + body += "<p><b>Context path:</b> " + getServletContext().getContextPath() + "</p>"; + if (exception != null) { + body += "<p><b>Cause:</b> " + exception.getCause() + "</p>"; + } + break; + } + request.setAttribute("appUrl", appUrl); + request.setAttribute("body", body); + request.setAttribute("locale", locale.getLanguage()); + request.setAttribute("statusCode", response.getStatus()); + request.setAttribute("title", title); + + response.setContentType("text/html;charset=UTF-8"); + getServletContext().getRequestDispatcher("/WEB-INF/error.jsp").forward(request, response); + } +} diff --git a/www-server/src/main/resources/fr/agrometinfo/www/server/i18n.properties b/www-server/src/main/resources/fr/agrometinfo/www/server/i18n.properties index e3e82d9..1f20f45 100644 --- a/www-server/src/main/resources/fr/agrometinfo/www/server/i18n.properties +++ b/www-server/src/main/resources/fr/agrometinfo/www/server/i18n.properties @@ -5,3 +5,8 @@ error.MAIL.SEND_FAILED=The e-mail "{0}" for the recipients {1} was not sent: {2} error.MAIL.SEND_FAILED_INVALID=the e-mail "{0}" was not sent to these invalid addresses: {1}. error.MAIL.SEND_FAILED_VALID=The e-mail "{0}" was not sent to these valid addresses: {1}. error.START=Start +http.status.title=Error +http.status.title[\=400]=Bad request +http.status.title[\=404]=Document not found +http.status.title[\=500]=Internal server error +http.status.title[\=503]=Service unavailable diff --git a/www-server/src/main/resources/fr/agrometinfo/www/server/i18n_fr.properties b/www-server/src/main/resources/fr/agrometinfo/www/server/i18n_fr.properties index ef082e9..b26453f 100644 --- a/www-server/src/main/resources/fr/agrometinfo/www/server/i18n_fr.properties +++ b/www-server/src/main/resources/fr/agrometinfo/www/server/i18n_fr.properties @@ -5,3 +5,8 @@ error.MAIL.SEND_FAILED=Le courriel « {0} » dont les destinataires sont {1} n'a error.MAIL.SEND_FAILED_INVALID=Le courriel « {0} » n'a pas été envoyé à ces adresses qui sont invalides : {1}. error.MAIL.SEND_FAILED_VALID=Le courriel « {0} » n'a pas été envoyé à ces adresses qui sont valides : {1}. error.START=Démarrage +http.status.title=Erreur +http.status.title[\=400]=Mauvaise demande +http.status.title[\=404]=Document non trouvé +http.status.title[\=500]=Erreur interne du serveur +http.status.title[\=503]=Service indisponible diff --git a/www-server/src/main/resources/fr/agrometinfo/www/server/jsp.properties b/www-server/src/main/resources/fr/agrometinfo/www/server/jsp.properties new file mode 100644 index 0000000..dd42edf --- /dev/null +++ b/www-server/src/main/resources/fr/agrometinfo/www/server/jsp.properties @@ -0,0 +1,9 @@ +# Ce fichier est encodé en UTF-8 +page.cookies=Cookies +page.error=Error page +page.legal-notice=Legal notice +page.privacy=Privacy policy +page.credits=Credits +page.citation=Citations +page.release-notes=Release notes +page.contact=Contact us diff --git a/www-server/src/main/resources/fr/agrometinfo/www/server/jsp_fr.properties b/www-server/src/main/resources/fr/agrometinfo/www/server/jsp_fr.properties new file mode 100644 index 0000000..1092f58 --- /dev/null +++ b/www-server/src/main/resources/fr/agrometinfo/www/server/jsp_fr.properties @@ -0,0 +1,9 @@ +# Ce fichier est encodé en UTF-8 +page.cookies=Cookies +page.error=Page d'erreur +page.legal-notice=Mentions légales +page.privacy=Politique de confidentialité +page.credits=Crédits +page.citation=Citations +page.release-notes=Notes de version +page.contact=Contactez-nous diff --git a/www-server/src/main/webapp/WEB-INF/error.jsp b/www-server/src/main/webapp/WEB-INF/error.jsp new file mode 100644 index 0000000..c060ee8 --- /dev/null +++ b/www-server/src/main/webapp/WEB-INF/error.jsp @@ -0,0 +1,12 @@ +<%@page language="java" isErrorPage="true" + contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> +<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> +<fmt:setLocale value="${locale}" /> +<fmt:setBundle basename="fr.agrometinfo.www.server.jsp" /> +<%@taglib prefix="t" tagdir="/WEB-INF/tags" %> +<t:page> + <jsp:attribute name="appUrl">${appUrl}</jsp:attribute> + <jsp:attribute name="locale">${locale}</jsp:attribute> + <jsp:attribute name="title"><fmt:message key="page.error" /> ${statusCode} − ${title}</jsp:attribute> + <jsp:body>${body}</jsp:body> +</t:page> diff --git a/www-server/src/main/webapp/WEB-INF/tags/page.tag b/www-server/src/main/webapp/WEB-INF/tags/page.tag new file mode 100644 index 0000000..b54979f --- /dev/null +++ b/www-server/src/main/webapp/WEB-INF/tags/page.tag @@ -0,0 +1,84 @@ +<%@tag description="Overall Page template" pageEncoding="UTF-8"%> +<%@attribute name="appUrl" required="true" %> +<%@attribute name="locale" required="true" %> +<%@attribute name="title" required="true" %> +<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> +<fmt:setLocale value="${locale}" /> +<fmt:setBundle basename="fr.agrometinfo.www.server.jsp" /> +<!DOCTYPE html> +<html lang="fr" dir="ltr"> + <head> + <meta charset="utf-8"> + <meta name="viewport" content="height=device-height, width=device-width, initial-scale=1.0, minimum-scale=1.0"> + <meta name="description" content="AgroMetInfo - mise à disposition d’indicateurs agroclimatiques et des indicateurs de suivi de culture d’hiver (culture type blé tendre) et de printemps (culture type maïs) par le modèle STICS en temps réel sous forme de cartes et de graphiques."> + <link rel="license" href="https://www.etalab.gouv.fr/licence-ouverte-open-licence/"> + <link href="${appUrl}images/favicon.ico" rel="icon" type="image/x-icon" sizes="any"> + <link href="${appUrl}css/fontawesome-all.min.css" rel="stylesheet" media="print" onload="this.media = 'all'; + this.onload = null;"> + <link href="${appUrl}css/home.css" rel="stylesheet"> + <link href="${appUrl}css/fonts.css" rel="stylesheet" media="print" onload="this.media = 'all'; + this.onload = null;"> + <title>AgroMetInfo − ${title}</title> + <script> + const baseUriFull='${appUrl}'; + const localStorageThemeKey = baseUriFull + 'variant'; + function setTheme(theme) { + document.documentElement.setAttribute("data-theme", theme); + localStorage.setItem(localStorageThemeKey, "zen-" + theme); + var classes = "fas fa-toggle-on"; + if (theme == "dark") { + classes = "fas fa-toggle-off"; + } + var elem = document.getElementById("toggle-theme").className = classes; + } + function getTheme() { + var theme = "light"; + if (localStorage.getItem(localStorageThemeKey)) { + if (localStorage.getItem(localStorageThemeKey) == "zen-dark") { + theme = "dark"; + } + } else if (document.documentElement.getAttribute("data-theme")) { + if (document.documentElement.getAttribute("data-theme") == "dark") { + theme = "dark"; + } + } else if(window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches) { + theme = "dark"; + } + return theme; + } + function toggleTheme() { + const theme = getTheme(); + if (theme === 'dark') { + setTheme('light'); + } else { + setTheme('dark'); + } + } + setTheme(getTheme()); + </script> + </head> + <body> + <nav> + <ul> + <li class="brand"> + <a href="${appUrl}"><img alt="logo" src="${appUrl}images/logo_etat-agrometinfo.svg" /></a> + </li> + </ul> + </nav> + <article> + <jsp:doBody/> + </article> + <footer> + <ul> + <li><a href="${appUrl}cookies-use.html"><fmt:message key="page.cookies" /></a></li> + <li><a href="${appUrl}legal-notice.html"><fmt:message key="page.legal-notice" /></a></li> + <li><a href="${appUrl}privacy.html"><fmt:message key="page.privacy" /></a></li> + <li><a href="${appUrl}credits.html"><fmt:message key="page.credits" /></a></li> + <li><a href="${appUrl}citation.html"><fmt:message key="page.citation" /></a></li> + <li><a href="${appUrl}release-notes.html"><fmt:message key="page.release-notes" /></a></li> + <li><a href="${appUrl}contact.html"><fmt:message key="page.contact" /></a></li> + </ul> + <div class="license">Sauf mention contraire, tous les textes de ce site sont sous <a href="https://www.etalab.gouv.fr/licence-ouverte-open-licence/" target="_blank">licence etalab-2.0</a>.</div> + </footer> + </body> +</html> diff --git a/www-server/src/main/webapp/WEB-INF/web.xml b/www-server/src/main/webapp/WEB-INF/web.xml index aa82e1c..1687cf8 100644 --- a/www-server/src/main/webapp/WEB-INF/web.xml +++ b/www-server/src/main/webapp/WEB-INF/web.xml @@ -38,6 +38,9 @@ <servlet-name>OpenApi</servlet-name> <url-pattern>/openapi/*</url-pattern> </servlet-mapping> + <error-page> + <location>/errorHandler</location> + </error-page> <!-- Default page to serve --> <welcome-file-list> <welcome-file>index.html</welcome-file> -- GitLab From 0e44c332a3cfb457a53e785d9ac491b8561fe84b Mon Sep 17 00:00:00 2001 From: Olivier Maury <Olivier.Maury@inrae.fr> Date: Mon, 22 Apr 2024 17:13:16 +0200 Subject: [PATCH 2/2] Personnaliser les pages d'erreur. fixes #39 --- pom.xml | 2 +- www-client/pom.xml | 2 +- www-server/pom.xml | 2 +- www-shared/pom.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 1ef5fe5..838689e 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>fr.agrometinfo</groupId> <artifactId>www</artifactId> - <version>2.0.0-alpha-2</version> + <version>2.0.0-SNAPSHOT</version> <packaging>pom</packaging> <name>AgroMetInfo web app</name> <description>Website for AgroMetInfo in Jakarta EE 10 and GWT.</description> diff --git a/www-client/pom.xml b/www-client/pom.xml index 384a0dc..f937fee 100644 --- a/www-client/pom.xml +++ b/www-client/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>fr.agrometinfo</groupId> <artifactId>www</artifactId> - <version>2.0.0-alpha-2</version> + <version>2.0.0-SNAPSHOT</version> </parent> <artifactId>www-client</artifactId> diff --git a/www-server/pom.xml b/www-server/pom.xml index a98d207..37ecee7 100644 --- a/www-server/pom.xml +++ b/www-server/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>fr.agrometinfo</groupId> <artifactId>www</artifactId> - <version>2.0.0-alpha-2</version> + <version>2.0.0-SNAPSHOT</version> </parent> <artifactId>www-server</artifactId> diff --git a/www-shared/pom.xml b/www-shared/pom.xml index 6d48e0d..205c0f1 100644 --- a/www-shared/pom.xml +++ b/www-shared/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>fr.agrometinfo</groupId> <artifactId>www</artifactId> - <version>2.0.0-alpha-2</version> + <version>2.0.0-SNAPSHOT</version> </parent> <artifactId>www-shared</artifactId> -- GitLab