diff --git a/www-server/pom.xml b/www-server/pom.xml index e331e74292668be858c45819f883e9a2b832d98a..e58d7e3e97621f64049b0dfb31a789f46e01d4fc 100644 --- a/www-server/pom.xml +++ b/www-server/pom.xml @@ -150,6 +150,11 @@ <artifactId>hibernate-hikaricp</artifactId> <version>${hibernate.version}</version> </dependency> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-slf4j2-impl</artifactId> + <version>${log4j.version}</version> + </dependency> <dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> @@ -201,6 +206,12 @@ <artifactId>jersey-test-framework-provider-grizzly2</artifactId> <scope>test</scope> </dependency> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-jul</artifactId> + <version>${log4j.version}</version> + <scope>test</scope> + </dependency> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> @@ -255,8 +266,9 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <configuration> - <argLine> + <configuration> + <argLine> +-Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.math=ALL-UNNAMED --add-opens=java.base/java.net=ALL-UNNAMED @@ -264,7 +276,7 @@ --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.util.concurrent=ALL-UNNAMED --add-opens=java.sql/java.sql=ALL-UNNAMED - </argLine> + </argLine> </configuration> </plugin> <plugin> diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/AgroMetInfoConfiguration.java b/www-server/src/main/java/fr/agrometinfo/www/server/AgroMetInfoConfiguration.java index a50505510636975bc20a9251bfeca6ae98d14732..0b812a8e02a93c8fd0d2241fb4e8eadce5df1868 100644 --- a/www-server/src/main/java/fr/agrometinfo/www/server/AgroMetInfoConfiguration.java +++ b/www-server/src/main/java/fr/agrometinfo/www/server/AgroMetInfoConfiguration.java @@ -27,6 +27,10 @@ public class AgroMetInfoConfiguration { * Keys for context.xml. */ public enum ConfigurationKey { + /** + * The e-mail address used to send e-mails. + */ + APP_EMAIL("app.email"), /** * The application URL. */ @@ -39,10 +43,6 @@ public class AgroMetInfoConfiguration { * Target environment (dev, preprod, prod). */ ENVIRONMENT("environment"), - /** - * The e-mail address used to send e-mails. - */ - APP_EMAIL("app.email"), /** * The e-mail address receiving the logs. */ @@ -51,6 +51,10 @@ public class AgroMetInfoConfiguration { * SMTP server name. */ SMTP_HOST("smtp.host"), + /** + * SMTP password. + */ + SMTP_PASSWORD("smtp.password"), /** * SMTP server port. */ @@ -59,10 +63,6 @@ public class AgroMetInfoConfiguration { * SMTP username. */ SMTP_USER("smtp.user"), - /** - * SMTP password. - */ - SMTP_PASSWORD("smtp.password"), /** * E-mail address for support. */ @@ -83,6 +83,16 @@ public class AgroMetInfoConfiguration { } } + /** + * Environment value. + */ + private static final String ENVIRONMENT_DEV = "dev"; + + /** + * Environment value. + */ + private static final String ENVIRONMENT_PROD = "prod"; + /** * Key prefix for the configuration in context.xml. */ @@ -133,17 +143,31 @@ public class AgroMetInfoConfiguration { } final var dir = new File(getCacheDirectory()); if (!dir.exists()) { - LOGGER.info("Creating directory {}", dir.getAbsolutePath()); + LOGGER.atInfo().log("Creating directory {}", dir.getAbsolutePath()); if (!dir.mkdirs()) { LOGGER.fatal("Cache directory {} failed to create!", dir.getAbsolutePath()); } } } + /** + * @return if configuration is set to development. + */ + public boolean isDevEnvironment() { + return ENVIRONMENT_DEV.equals(get(ConfigurationKey.ENVIRONMENT)); + } + + /** + * @return if configuration is set to production. + */ + public boolean isProdEnvironment() { + return ENVIRONMENT_PROD.equals(get(ConfigurationKey.ENVIRONMENT)); + } + /** * Set value, only for testing purpose. * - * @param key key + * @param key key * @param value value */ public void set(@NonNull final ConfigurationKey key, final String value) { 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 index d8ac555b2501e4d230d9aeb63bfced5478e86485..50792f3791aad44d4ee029fe285ef352953ee708 100644 --- a/www-server/src/main/java/fr/agrometinfo/www/server/ErrorHandlerServlet.java +++ b/www-server/src/main/java/fr/agrometinfo/www/server/ErrorHandlerServlet.java @@ -53,7 +53,7 @@ public final class ErrorHandlerServlet extends HttpServlet { "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> + /contact.html">contactez-nous</a>.</li> </ul> """; case HttpServletResponse.SC_INTERNAL_SERVER_ERROR -> diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/I18n.java b/www-server/src/main/java/fr/agrometinfo/www/server/I18n.java index fac4a9558bee193672d6794da646624ff8905d51..1140dddb529ebfaffbf50af8df56e83446738341 100644 --- a/www-server/src/main/java/fr/agrometinfo/www/server/I18n.java +++ b/www-server/src/main/java/fr/agrometinfo/www/server/I18n.java @@ -10,7 +10,6 @@ import java.util.ResourceBundle; import java.util.Set; import java.util.function.BiFunction; import java.util.function.BiPredicate; -import java.util.stream.Collectors; import lombok.Getter; import lombok.NonNull; @@ -199,7 +198,7 @@ public class I18n { final var suffixes = getKeys().stream() .filter(k -> k.startsWith(key + "[") && k.endsWith("]")) .map(k -> k.substring(key.length() + 1, k.length() - 1)) - .collect(Collectors.toList()); + .toList(); for (final String suf : suffixes) { if (matches(suf, plural)) { keyWithSuffix = key + "[" + suf + "]"; diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/SessionAttribute.java b/www-server/src/main/java/fr/agrometinfo/www/server/SessionAttribute.java index 6d30da3787d85055e61e9d9ff4bbbf1612c8ae8a..1f09750cd87fb11ac17ef79e04290ee43f7518b0 100644 --- a/www-server/src/main/java/fr/agrometinfo/www/server/SessionAttribute.java +++ b/www-server/src/main/java/fr/agrometinfo/www/server/SessionAttribute.java @@ -1,7 +1,3 @@ -/* - * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license - * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Enum.java to edit this template - */ package fr.agrometinfo.www.server; /** diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/StartListener.java b/www-server/src/main/java/fr/agrometinfo/www/server/StartListener.java index 59336c889891fa8ca11914013dfc2e0b171db631..80f4497a14bc510ec694d04980fd67ae515d4616 100644 --- a/www-server/src/main/java/fr/agrometinfo/www/server/StartListener.java +++ b/www-server/src/main/java/fr/agrometinfo/www/server/StartListener.java @@ -5,7 +5,8 @@ import java.util.List; import fr.agrometinfo.www.server.exception.AgroMetInfoException; import fr.agrometinfo.www.server.service.MailService; -import fr.agrometinfo.www.server.util.AppVersion; +import fr.agrometinfo.www.server.util.ST; +import fr.agrometinfo.www.server.util.ST.Key; import jakarta.inject.Inject; import jakarta.servlet.ServletContextEvent; import jakarta.servlet.ServletContextListener; @@ -20,6 +21,15 @@ import lombok.extern.log4j.Log4j2; @Log4j2 @WebListener public final class StartListener implements ServletContextListener { + /** + * E-mail template. + */ + private static final String TEMPLATE = """ + Bonjour <SUPPORT_EMAIL>, + + AgroMetInfo-www-<VERSION> démarre. + <MESSAGE> + """; /** * Application configuration. */ @@ -34,18 +44,20 @@ public final class StartListener implements ServletContextListener { @Override public void contextInitialized(final ServletContextEvent event) { // Send an e-mail to log e-mail address at application start. - if (configuration.get(AgroMetInfoConfiguration.ConfigurationKey.ENVIRONMENT).equals("dev")) { + if (configuration.isDevEnvironment()) { LOGGER.trace("Environment is dev, e-mail isn't send"); return; } - var mail = new MailService.Mail(); - mail.setContent("Bonjour,\nAgroMetInfo-www-" + AppVersion.getVersion() + " démarre.\n" + LocalDateTime.now()); - mail.setFromAddress(configuration.get(AgroMetInfoConfiguration.ConfigurationKey.APP_EMAIL)); + final var mail = new MailService.Mail(); mail.setSubject("Démarrage"); mail.setToAddresses(List.of(configuration.get(AgroMetInfoConfiguration.ConfigurationKey.LOG_EMAIL))); + final ST st = new ST(TEMPLATE); + st.add(Key.SUPPORT_EMAIL, String.join(", ", mail.getToAddresses())); + st.add(Key.MESSAGE, String.valueOf(LocalDateTime.now())); + mail.setContent(st.render()); try { mailService.send(mail); - } catch (AgroMetInfoException e) { + } catch (final AgroMetInfoException e) { LOGGER.info("failed to send e-mail : {}", e); } } diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/dao/DailyValueDaoHibernate.java b/www-server/src/main/java/fr/agrometinfo/www/server/dao/DailyValueDaoHibernate.java index b417470b5966f48fa34553e07dcbd29db59494a6..84e4cbadb1007b83231fd85405f9f8fcc430f141 100644 --- a/www-server/src/main/java/fr/agrometinfo/www/server/dao/DailyValueDaoHibernate.java +++ b/www-server/src/main/java/fr/agrometinfo/www/server/dao/DailyValueDaoHibernate.java @@ -16,6 +16,7 @@ import jakarta.enterprise.context.ApplicationScoped; */ @ApplicationScoped public class DailyValueDaoHibernate extends DaoHibernate<DailyValue> implements DailyValueDao { + /** * Constructor. */ @@ -26,7 +27,7 @@ public class DailyValueDaoHibernate extends DaoHibernate<DailyValue> implements @Override public final List<DailyValue> find(final Indicator indicator, final LocalDate date) { final var jpql = "SELECT t FROM DailyValue AS t WHERE t.indicator=:indicator AND t.date=:date"; - return super.findAllByJPQL(jpql, Map.of("indicator", indicator, "date", date), DailyValue.class); + return super.findAllByJPQL(jpql, Map.of(PARAM_INDICATOR, indicator, PARAM_DATE, date), DailyValue.class); } @Override @@ -34,7 +35,7 @@ public class DailyValueDaoHibernate extends DaoHibernate<DailyValue> implements final var jpql = """ SELECT t FROM DailyValue AS t WHERE t.indicator=:indicator AND t.date=:date AND t.cell.department.region=:region"""; - return super.findAllByJPQL(jpql, Map.of("indicator", indicator, "date", date, "region", region), + return super.findAllByJPQL(jpql, Map.of(PARAM_INDICATOR, indicator, PARAM_DATE, date, PARAM_REGION, region), DailyValue.class); } @@ -44,7 +45,7 @@ public class DailyValueDaoHibernate extends DaoHibernate<DailyValue> implements SELECT AVG(t.comparedValue) FROM DailyValue AS t WHERE t.indicator=:indicator AND t.date=:date"""; - return super.findOneByJPQL(jpql, Map.of("indicator", indicator, "date", date), Double.class); + return super.findOneByJPQL(jpql, Map.of(PARAM_INDICATOR, indicator, PARAM_DATE, date), Double.class); } @Override @@ -55,7 +56,7 @@ public class DailyValueDaoHibernate extends DaoHibernate<DailyValue> implements FROM DailyValue AS t WHERE t.indicator=:indicator AND t.date=:date AND t.cell.department.region.id=:region AND t.computedValue IS NOT NULL"""; - return super.findOneByJPQL(jpql, Map.of("indicator", indicator, "date", date, "region", regionId), + return super.findOneByJPQL(jpql, Map.of(PARAM_INDICATOR, indicator, PARAM_DATE, date, PARAM_REGION, regionId), Double.class); } @@ -65,7 +66,7 @@ public class DailyValueDaoHibernate extends DaoHibernate<DailyValue> implements SELECT AVG(t.computedValue) FROM DailyValue AS t WHERE t.indicator=:indicator AND t.date=:date"""; - return super.findOneByJPQL(jpql, Map.of("indicator", indicator, "date", date), Double.class); + return super.findOneByJPQL(jpql, Map.of(PARAM_INDICATOR, indicator, PARAM_DATE, date), Double.class); } @Override @@ -76,7 +77,7 @@ public class DailyValueDaoHibernate extends DaoHibernate<DailyValue> implements FROM DailyValue AS t WHERE t.indicator=:indicator AND t.date=:date AND t.cell.department.region.id=:region AND t.computedValue IS NOT NULL"""; - return super.findOneByJPQL(jpql, Map.of("indicator", indicator, "date", date, "region", regionId), + return super.findOneByJPQL(jpql, Map.of(PARAM_INDICATOR, indicator, PARAM_DATE, date, PARAM_REGION, regionId), Double.class); } diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/dao/DaoHibernate.java b/www-server/src/main/java/fr/agrometinfo/www/server/dao/DaoHibernate.java index 814045d288b32a62555e6afc2af565194392895f..856d1ff41dfe027e66320c99ced69fdf4020e990 100644 --- a/www-server/src/main/java/fr/agrometinfo/www/server/dao/DaoHibernate.java +++ b/www-server/src/main/java/fr/agrometinfo/www/server/dao/DaoHibernate.java @@ -1,12 +1,5 @@ package fr.agrometinfo.www.server.dao; -import jakarta.persistence.NoResultException; -import jakarta.persistence.Query; -import jakarta.persistence.TypedQuery; -import jakarta.persistence.criteria.CriteriaBuilder; -import jakarta.persistence.criteria.CriteriaQuery; -import jakarta.persistence.criteria.Order; -import jakarta.persistence.criteria.Root; import java.math.BigDecimal; import java.sql.Connection; import java.sql.SQLException; @@ -14,8 +7,18 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.function.Consumer; +import java.util.function.Function; + +import org.hibernate.Session; + +import jakarta.persistence.NoResultException; +import jakarta.persistence.Query; +import jakarta.persistence.TypedQuery; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Order; +import jakarta.persistence.criteria.Root; import lombok.extern.log4j.Log4j2; -import org.hibernate.engine.spi.SessionImplementor; /** * Common methods for DAO with Hibernate. @@ -24,37 +27,27 @@ import org.hibernate.engine.spi.SessionImplementor; * @author Olivier Maury */ @Log4j2 -public abstract class DaoHibernate<T> { +class DaoHibernate<T> { /** - * Get the class name of JDBC driver. - * - * @param em Entity manager - * @return name of JDBC driver + * Order value. */ - protected static final String getDriver(final ScopedEntityManager em) { - final SessionImplementor sessionImp = (SessionImplementor) em.getDelegate(); - try { - final Connection conn = sessionImp.getJdbcConnectionAccess().obtainConnection(); - return conn.getMetaData().getDriverName(); - } catch (final SQLException e) { - return ""; - } - } - + private static final String ORDER_DESC = "DESC"; /** - * Checks if the database is connected by a PostgreSQL, useful to call specific - * SQL. - * - * @param em Entity manager - * @return if the SGBD is PostgreSQL + * Statement parameter. */ - protected static final boolean isPostgreSQLDriver(final ScopedEntityManager em) { - final var driverName = getDriver(em); - if (driverName == null || driverName.isBlank()) { - return false; - } - return driverName.startsWith("PostgreSQL"); - } + protected static final String PARAM_REGION = "region"; + /** + * Statement parameter. + */ + protected static final String PARAM_DATE = "date"; + /** + * Statement parameter. + */ + protected static final String PARAM_INDICATOR = "indicator"; + /** + * Statement parameter. + */ + protected static final String PARAM_YEAR = "year"; /** * Related class. @@ -84,6 +77,7 @@ public abstract class DaoHibernate<T> { return 0L; } } + /** * Delete all row in table. */ @@ -99,6 +93,22 @@ public abstract class DaoHibernate<T> { } LOGGER.traceExit(); } + + /** + * Use a function to execute direct operations on database with JDBC in a + * transaction. + * + * @param <U> class of function result + * @param function function with JDBC operations + * @return function result + */ + protected final <U> U doInJdbc(final Function<Connection, U> function) { + try (ScopedEntityManager em = getScopedEntityManager()) { + final Session session = em.unwrap(Session.class); + return session.doReturningWork(function::apply); + } + } + /** * Use a consumer to execute JPA operations in a transaction. * @@ -106,9 +116,7 @@ public abstract class DaoHibernate<T> { */ protected final void doInJpaTransaction(final Consumer<ScopedEntityManager> consumer) { try (ScopedEntityManager em = getScopedEntityManager()) { - em.executeTransaction(() -> { - consumer.accept(em); - }); + em.executeTransaction(() -> consumer.accept(em)); } } @@ -153,7 +161,7 @@ public abstract class DaoHibernate<T> { if (orders != null) { final List<Order> orderList = new ArrayList<>(); orders.forEach((column, asc) -> { - if ("DESC".equalsIgnoreCase(asc)) { + if (ORDER_DESC.equalsIgnoreCase(asc)) { orderList.add(cBuilder.desc(root.get(column))); } else { orderList.add(cBuilder.asc(root.get(column))); @@ -174,7 +182,6 @@ public abstract class DaoHibernate<T> { * Find all matching entities using JPQL and parameters for entities of the * specified class. * - * * @param <U> entity class * @param stmt JPQL statement * @param params parameters for the JPQL statement @@ -193,11 +200,11 @@ public abstract class DaoHibernate<T> { return new ArrayList<>(); } } + /** * Find all matching entities using SQL and parameters for entities of the * specified class. * - * * @param <U> entity class * @param sql SQL statement * @param params parameters for the SQL statement @@ -236,7 +243,6 @@ public abstract class DaoHibernate<T> { return null; } } - /** * Find the first result. * @@ -279,6 +285,21 @@ public abstract class DaoHibernate<T> { } } + /** + * Get the class name of JDBC driver. + * + * @return name of JDBC driver + */ + protected final String getDriver() { + return doInJdbc(conn -> { + try { + return conn.getMetaData().getDriverName(); + } catch (final SQLException e) { + return ""; + } + }); + } + /** * @return name of current schema. */ @@ -297,17 +318,17 @@ public abstract class DaoHibernate<T> { */ public BigDecimal getSchemaSize(final String schema) { try (ScopedEntityManager em = getScopedEntityManager()) { - if (isPostgreSQLDriver(em)) { + if (isPostgreSQLDriver()) { final String sql = """ - SELECT - SUM(pg_total_relation_size(CONCAT(quote_ident(schemaname), '.', quote_ident(tablename)))) - FROM pg_catalog.pg_tables WHERE schemaname=:schema - """; + SELECT + SUM(pg_total_relation_size(CONCAT(quote_ident(schemaname), '.', quote_ident(tablename)))) + FROM pg_catalog.pg_tables WHERE schemaname=:schema + """; final Query query = em.createNativeQuery(sql); query.setParameter("schema", schema); return (BigDecimal) query.getSingleResult(); } else { - throw new UnsupportedOperationException("Database driver not handled! " + getDriver(em)); + throw new UnsupportedOperationException("Database driver not handled! " + getDriver()); } } } @@ -318,6 +339,21 @@ public abstract class DaoHibernate<T> { protected final ScopedEntityManager getScopedEntityManager() { return PersistenceManager.getInstance().createScopedEntityManager(); } + + /** + * Checks if the database is connected by a PostgreSQL, useful to call specific + * SQL. + * + * @return if the SGBD is PostgreSQL + */ + protected final boolean isPostgreSQLDriver() { + final var driverName = getDriver(); + if (driverName == null || driverName.isBlank()) { + return false; + } + return driverName.startsWith("PostgreSQL"); + } + /** * Save a new entity in database. * @param object diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/dao/EntityManagerFactoryHandler.java b/www-server/src/main/java/fr/agrometinfo/www/server/dao/EntityManagerFactoryHandler.java index 0d1a8f2bcec09102634f32ebc1786e243e4a5527..c7b8950a0e8244cb85467f2e7334fecfd2eb96bb 100644 --- a/www-server/src/main/java/fr/agrometinfo/www/server/dao/EntityManagerFactoryHandler.java +++ b/www-server/src/main/java/fr/agrometinfo/www/server/dao/EntityManagerFactoryHandler.java @@ -46,24 +46,21 @@ public final class EntityManagerFactoryHandler implements InvocationHandler { private final EntityManagerFactory emf; @Override - public Object invoke(final Object o, final Method method, final Object[] os) - throws Throwable { + public Object invoke(final Object o, final Method method, final Object[] os) throws Throwable { LOGGER.traceEntry("enter {} {} {}", ScopedEntityManagerFactory.class.getCanonicalName(), method.getName(), os); Object result; try { if (method.equals(CREATE_SCOPED_ENTITY_MANAGER)) { - final EntityManager target = (EntityManager) CREATE_ENTITY_MANAGER - .invoke(emf, os); + final EntityManager target = (EntityManager) CREATE_ENTITY_MANAGER.invoke(emf, os); result = Proxy.newProxyInstance( - ScopedEntityManager.class.getClassLoader(), + Thread.currentThread().getContextClassLoader(), new Class[]{ScopedEntityManager.class}, new EntityManagerHandler(target)); } else { result = method.invoke(emf, os); } - } catch (final IllegalAccessException | IllegalArgumentException - | InvocationTargetException e) { + } catch (final IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { throw LOGGER.throwing(e); } if (method.getReturnType().getClass().equals(Void.class.getClass())) { diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/dao/EntityManagerHandler.java b/www-server/src/main/java/fr/agrometinfo/www/server/dao/EntityManagerHandler.java index a760d69f7907f883a66b864eba495a7245da80b1..9566e3eb5bfc4d824e2e0af659f0bdac99b45020 100644 --- a/www-server/src/main/java/fr/agrometinfo/www/server/dao/EntityManagerHandler.java +++ b/www-server/src/main/java/fr/agrometinfo/www/server/dao/EntityManagerHandler.java @@ -57,7 +57,7 @@ public final class EntityManagerHandler implements AutoCloseable, InvocationHand throws Throwable { LOGGER.traceEntry("enter {} {} {}", ScopedEntityManagerFactory.class.getCanonicalName(), method.getName(), os); - Object result; + final Object result; try { if (method.equals(EXECUTE_TRANSACTION) || method.equals(EXECUTE_VOID_TRANSACTION)) { @@ -65,8 +65,7 @@ public final class EntityManagerHandler implements AutoCloseable, InvocationHand } else { result = method.invoke(em, os); } - } catch (final IllegalAccessException | IllegalArgumentException - | InvocationTargetException e) { + } catch (final IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { throw LOGGER.throwing(e); } if (method.getReturnType().getClass().equals(Void.class.getClass())) { diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/dao/PersistenceManager.java b/www-server/src/main/java/fr/agrometinfo/www/server/dao/PersistenceManager.java index 2f58b77fed032a27d1e818891c94a87f6de40eaf..e7cedbb17bc74f2ae082cb6c470beb9d68e3f63c 100644 --- a/www-server/src/main/java/fr/agrometinfo/www/server/dao/PersistenceManager.java +++ b/www-server/src/main/java/fr/agrometinfo/www/server/dao/PersistenceManager.java @@ -63,7 +63,7 @@ public enum PersistenceManager { final EntityManagerFactory emf = Persistence.createEntityManagerFactory("agrometinfo"); Objects.requireNonNull(emf, "EntityManagerFactory must be found for 'agrometinfo'!"); semf = (ScopedEntityManagerFactory) Proxy.newProxyInstance( - ScopedEntityManagerFactory.class.getClassLoader(), + Thread.currentThread().getContextClassLoader(), new Class[] {ScopedEntityManagerFactory.class}, new EntityManagerFactoryHandler(emf)); } diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/dao/PraDailyValueDaoHibernate.java b/www-server/src/main/java/fr/agrometinfo/www/server/dao/PraDailyValueDaoHibernate.java index 2759837ed44dd1c7267209b5c33aa055101f1c26..28a1044a98765ca0b736280b410d4943f088d352 100644 --- a/www-server/src/main/java/fr/agrometinfo/www/server/dao/PraDailyValueDaoHibernate.java +++ b/www-server/src/main/java/fr/agrometinfo/www/server/dao/PraDailyValueDaoHibernate.java @@ -29,6 +29,11 @@ import lombok.NonNull; @ApplicationScoped public class PraDailyValueDaoHibernate extends DaoHibernate<PraDailyValue> implements PraDailyValueDao { + /** + * Statement parameter. + */ + private static final String PARAM_START = "start"; + private static Float toFloat(final Tuple t, final String column) { if (t.get(column) == null) { return null; @@ -36,7 +41,7 @@ public class PraDailyValueDaoHibernate extends DaoHibernate<PraDailyValue> imple return ((Number) t.get(column)).floatValue(); } - private static PraDailyValue toPraDailyValue(final Object[] row) { + private static PraDailyValue toPraDailyValue(final Object... row) { final var value = new PraDailyValue(); int i = 0; value.setDate(DateUtils.toLocalDate((Date) row[i++])); @@ -76,7 +81,7 @@ public class PraDailyValueDaoHibernate extends DaoHibernate<PraDailyValue> imple @Override public final List<PraDailyValue> find(final Indicator indicator, final LocalDate date) { - final Map<String, Object> params = Map.of("indicator", indicator.getId(), "date", date); + final Map<String, Object> params = Map.of(PARAM_INDICATOR, indicator.getId(), PARAM_DATE, date); final var sql = """ SELECT v.date, @@ -95,12 +100,13 @@ public class PraDailyValueDaoHibernate extends DaoHibernate<PraDailyValue> imple @Override public final PraDailyValue find(final Indicator indicator, final LocalDate date, final Pra pra) { final var jpql = "SELECT t FROM PraDailyValue t WHERE t.indicator=:indicator AND t.date=:date AND t.pra=:pra"; - return super.findOneByJPQL(jpql, Map.of("indicator", indicator, "date", date, "pra", pra), PraDailyValue.class); + return super.findOneByJPQL(jpql, Map.of(PARAM_INDICATOR, indicator, PARAM_DATE, date, "pra", pra), + PraDailyValue.class); } @Override public final List<PraDailyValue> find(final Indicator indicator, final Region region, final LocalDate date) { - final Map<String, Object> params = Map.of("indicator", indicator.getId(), "date", date, "region", + final Map<String, Object> params = Map.of(PARAM_INDICATOR, indicator.getId(), PARAM_DATE, date, PARAM_REGION, region.getId()); final var sql = """ SELECT @@ -124,7 +130,7 @@ public class PraDailyValueDaoHibernate extends DaoHibernate<PraDailyValue> imple SELECT AVG(t.comparedValue) FROM PraDailyValue AS t WHERE t.indicator=:indicator AND t.date=:date"""; - return super.findOneByJPQL(jpql, Map.of("indicator", indicator, "date", date), Double.class); + return super.findOneByJPQL(jpql, Map.of(PARAM_INDICATOR, indicator, PARAM_DATE, date), Double.class); } @Override @@ -135,7 +141,7 @@ public class PraDailyValueDaoHibernate extends DaoHibernate<PraDailyValue> imple FROM PraDailyValue AS t WHERE t.indicator=:indicator AND t.date=:date AND t.pra.department.region.id=:region AND t.computedValue IS NOT NULL"""; - return super.findOneByJPQL(jpql, Map.of("indicator", indicator, "date", date, "region", regionId), + return super.findOneByJPQL(jpql, Map.of(PARAM_INDICATOR, indicator, PARAM_DATE, date, PARAM_REGION, regionId), Double.class); } @@ -145,7 +151,7 @@ public class PraDailyValueDaoHibernate extends DaoHibernate<PraDailyValue> imple SELECT AVG(t.computedValue) FROM PraDailyValue AS t WHERE t.indicator=:indicator AND t.date=:date"""; - return super.findOneByJPQL(jpql, Map.of("indicator", indicator, "date", date), Double.class); + return super.findOneByJPQL(jpql, Map.of(PARAM_INDICATOR, indicator, PARAM_DATE, date), Double.class); } @Override @@ -156,7 +162,7 @@ public class PraDailyValueDaoHibernate extends DaoHibernate<PraDailyValue> imple FROM PraDailyValue AS t WHERE t.indicator=:indicator AND t.date=:date AND t.pra.department.region.id=:region AND t.computedValue IS NOT NULL"""; - return super.findOneByJPQL(jpql, Map.of("indicator", indicator, "date", date, "region", regionId), + return super.findOneByJPQL(jpql, Map.of(PARAM_INDICATOR, indicator, PARAM_DATE, date, PARAM_REGION, regionId), Double.class); } @@ -176,8 +182,8 @@ public class PraDailyValueDaoHibernate extends DaoHibernate<PraDailyValue> imple ORDER BY t.date ASC """; final Map<String, Object> params = Map.of(// - "indicator", indicator, // - "start", start, // + PARAM_INDICATOR, indicator, // + PARAM_START, start, // "end", end); return findDailyValues(jpql, params); } @@ -198,9 +204,9 @@ public class PraDailyValueDaoHibernate extends DaoHibernate<PraDailyValue> imple ORDER BY t.date ASC """; final Map<String, Object> params = Map.of(// - "indicator", indicator, // + PARAM_INDICATOR, indicator, // "pra", pra, // - "start", start, // + PARAM_START, start, // "end", end); return findDailyValues(jpql, params); } @@ -221,9 +227,9 @@ public class PraDailyValueDaoHibernate extends DaoHibernate<PraDailyValue> imple ORDER BY t.date ASC """; final Map<String, Object> params = Map.of(// - "indicator", indicator, // - "region", region, // - "start", start, // + PARAM_INDICATOR, indicator, // + PARAM_REGION, region, // + PARAM_START, start, // "end", end); return findDailyValues(jpql, params); } @@ -236,7 +242,7 @@ public class PraDailyValueDaoHibernate extends DaoHibernate<PraDailyValue> imple } return query.getResultStream()// .collect(Collectors.toMap(// - t -> (LocalDate) t.get("date"), // + t -> (LocalDate) t.get(PARAM_DATE), // t -> { final Float[] values = new Float[SummaryDTO.Q95_INDEX + 1]; values[SummaryDTO.COMPUTED_INDEX] = ((Number) t.get("value")).floatValue(); @@ -254,7 +260,7 @@ public class PraDailyValueDaoHibernate extends DaoHibernate<PraDailyValue> imple @Override public final List<Indicator> findIndicators(@NonNull final Integer year) { final var sql = "SELECT DISTINCT indicator FROM v_pra_dailyvalue WHERE EXTRACT(YEAR FROM date) = :year"; - final List<Long> ids = super.findAllBySQL(sql, Map.of("year", year)).stream() // + final List<Long> ids = super.findAllBySQL(sql, Map.of(PARAM_YEAR, year)).stream() // .map(i -> Long.valueOf((int) i)).toList(); final var jpql = "SELECT i FROM Indicator AS i JOIN i.period WHERE i.id IN (:ids)"; return super.findAllByJPQL(jpql, Map.of("ids", ids), Indicator.class); @@ -266,7 +272,7 @@ public class PraDailyValueDaoHibernate extends DaoHibernate<PraDailyValue> imple SELECT MAX(t.date) FROM PraDailyValue AS t WHERE t.indicator=:indicator AND EXTRACT(YEAR FROM t.date) = :year"""; - return super.findOneByJPQL(jpql, Map.of("indicator", indicator, "year", year), LocalDate.class); + return super.findOneByJPQL(jpql, Map.of(PARAM_INDICATOR, indicator, PARAM_YEAR, year), LocalDate.class); } private List<PraDailyValue> findPraDailyValues(final String sql, final Map<String, Object> params) { @@ -305,7 +311,7 @@ public class PraDailyValueDaoHibernate extends DaoHibernate<PraDailyValue> imple JOIN d.region AS r WHERE EXTRACT(YEAR FROM t.date) = :year ORDER BY r.name"""; - return super.findAllByJPQL(jpql, Map.of("year", year), Region.class); + return super.findAllByJPQL(jpql, Map.of(PARAM_YEAR, year), Region.class); } @Override diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/exception/ErrorMessage.java b/www-server/src/main/java/fr/agrometinfo/www/server/exception/ErrorMessage.java index 8f472784fcfccdbd145d9ebce92ff35047d1fca8..4d530a7284ea201828728e606484d84f01d63361 100644 --- a/www-server/src/main/java/fr/agrometinfo/www/server/exception/ErrorMessage.java +++ b/www-server/src/main/java/fr/agrometinfo/www/server/exception/ErrorMessage.java @@ -1,9 +1,10 @@ package fr.agrometinfo.www.server.exception; -import fr.agrometinfo.www.server.I18n; import java.io.Serializable; import java.util.Collection; import java.util.Locale; + +import fr.agrometinfo.www.server.I18n; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.RequiredArgsConstructor; @@ -98,11 +99,10 @@ public final class ErrorMessage implements Serializable { */ public String toJSON() { final var sb = new StringBuilder(); - sb.append("{"); - sb.append("\"bundleName\": \"").append(bundleName).append("\", "); - sb.append("\"errorCode\": \"").append(type.getFullCode()).append("\", "); - sb.append("\"errorName\": \"").append(type.getName()).append("\", "); - sb.append("\"arguments\": ["); + sb.append("{\"bundleName\": \"").append(bundleName); + sb.append("\", \"errorCode\": \"").append(type.getFullCode()); + sb.append("\", \"errorName\": \"").append(type.getName()); + sb.append("\", \"arguments\": ["); if (arguments != null) { boolean first = true; for (final Object arg : arguments) { @@ -111,11 +111,11 @@ public final class ErrorMessage implements Serializable { } else { sb.append(", "); } - sb.append("\"") + sb.append('"') .append(arg.toString() .replace("\"", "\\\"") .replace("'", "\\'")) - .append("\""); + .append('"'); } } sb.append("]}"); diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/model/Cell.java b/www-server/src/main/java/fr/agrometinfo/www/server/model/Cell.java index acd8208548a871d9f11020eed1911393fefe2559..e42fa28058e60cb650a359d1dd46846fd827995a 100644 --- a/www-server/src/main/java/fr/agrometinfo/www/server/model/Cell.java +++ b/www-server/src/main/java/fr/agrometinfo/www/server/model/Cell.java @@ -21,6 +21,10 @@ import lombok.Data; @Entity @Table(name = "cell") public class Cell { + /** + * Column definition. + */ + private static final String NUMERIC_16_14 = "NUMERIC(16,14)"; /** * Creation date. */ @@ -46,22 +50,22 @@ public class Cell { /** * Latitude of point 1 [°N]. */ - @Column(columnDefinition = "NUMERIC(16,14)") + @Column(columnDefinition = NUMERIC_16_14) private Double lat1; /** * Latitude of point 2 [°N]. */ - @Column(columnDefinition = "NUMERIC(16,14)") + @Column(columnDefinition = NUMERIC_16_14) private Double lat2; /** * Latitude of point 3 [°N]. */ - @Column(columnDefinition = "NUMERIC(16,14)") + @Column(columnDefinition = NUMERIC_16_14) private Double lat3; /** * Latitude of point 4 [°N]. */ - @Column(columnDefinition = "NUMERIC(16,14)") + @Column(columnDefinition = NUMERIC_16_14) private Double lat4; /** * Longitude of cell center [°E]. @@ -70,22 +74,22 @@ public class Cell { /** * Longitude of point 1 [°E]. */ - @Column(columnDefinition = "NUMERIC(16,14)") + @Column(columnDefinition = NUMERIC_16_14) private Double lon1; /** * Longitude of point 2 [°E]. */ - @Column(columnDefinition = "NUMERIC(16,14)") + @Column(columnDefinition = NUMERIC_16_14) private Double lon2; /** * Longitude of point 3 [°E]. */ - @Column(columnDefinition = "NUMERIC(16,14)") + @Column(columnDefinition = NUMERIC_16_14) private Double lon3; /** * Longitude of point 4 [°E]. */ - @Column(columnDefinition = "NUMERIC(16,14)") + @Column(columnDefinition = NUMERIC_16_14) private Double lon4; } diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/rs/IndicatorResource.java b/www-server/src/main/java/fr/agrometinfo/www/server/rs/IndicatorResource.java index 860128907cf0084cd64ef306a464e6c60bbe8531..4783deeb54c8acd5bf464d2052f1f31a36a614fb 100644 --- a/www-server/src/main/java/fr/agrometinfo/www/server/rs/IndicatorResource.java +++ b/www-server/src/main/java/fr/agrometinfo/www/server/rs/IndicatorResource.java @@ -61,6 +61,40 @@ import lombok.extern.log4j.Log4j2; @Path(IndicatorService.PATH) @RequestScoped public class IndicatorResource implements IndicatorService { + /** + * Query parameter. + */ + private static final String PARAM_COMPARISON = "comparison"; + + /** + * Query parameter. + */ + private static final String PARAM_ID = "id"; + + /** + * Query parameter. + */ + private static final String PARAM_INDICATOR = "indicator"; + + /** + * Query parameter. + */ + private static final String PARAM_LEVEL = "level"; + + /** + * Query parameter. + */ + private static final String PARAM_PERIOD = "period"; + + /** + * Query parameter. + */ + private static final String PARAM_REGION = "region"; + + /** + * Query parameter. + */ + private static final String PARAM_YEAR = "year"; /** * Get the date related to the year. @@ -141,10 +175,10 @@ public class IndicatorResource implements IndicatorService { private CacheService cacheService; /** - * DAO for {@link PraDailyValue}. + * HTTP headers for response. */ - @Inject - private PraDailyValueDao praDailyValueDao; + @Context + private HttpHeaders httpHeaders; /** * Information for HTTP servlet. @@ -158,6 +192,12 @@ public class IndicatorResource implements IndicatorService { @Inject private IndicatorDao indicatorDao; + /** + * DAO for {@link PraDailyValue}. + */ + @Inject + private PraDailyValueDao praDailyValueDao; + /** * DAO for {@link Pra}. */ @@ -176,12 +216,6 @@ public class IndicatorResource implements IndicatorService { @Context private Request request; - /** - * HTTP headers for response. - */ - @Context - private HttpHeaders httpHeaders; - /** * @param collection collection to annotate * @param indicator indicator with metadata to add @@ -213,13 +247,7 @@ public class IndicatorResource implements IndicatorService { */ private void checkRequired(final Object value, final String queryParamName) { if (value instanceof final String str && str.isBlank() || value == null) { - final var status = Response.Status.BAD_REQUEST; - throw new WebApplicationException(// - Response.status(status) // - .entity(ErrorResponseDTO.of(status.getStatusCode(), // - status.getReasonPhrase(), // - queryParamName + " parameter is mandatory")) // - .build()); + throwWebApplicationException(Response.Status.BAD_REQUEST, queryParamName + " parameter is mandatory"); } } @@ -235,7 +263,7 @@ public class IndicatorResource implements IndicatorService { @Path(PATH_LIST) @Produces(MediaType.APPLICATION_JSON) @Override - public List<PeriodDTO> getPeriods(@QueryParam(value = "year") @NonNull final Integer year) { + public List<PeriodDTO> getPeriods(@QueryParam(value = PARAM_YEAR) @NonNull final Integer year) { LOGGER.traceEntry(); final var locale = LocaleUtils.getLocale(httpServletRequest); // HTTP cache headers @@ -279,7 +307,7 @@ public class IndicatorResource implements IndicatorService { @Path(PATH_REGIONS) @Produces(MediaType.APPLICATION_JSON) @Override - public Map<String, String> getRegions(@QueryParam(value = "year") @NonNull final Integer year) { + public Map<String, String> getRegions(@QueryParam(value = PARAM_YEAR) @NonNull final Integer year) { // HTTP cache headers cacheService.setCacheKey(PATH, PATH_REGIONS, year); cacheService.setHeaders(httpHeaders); @@ -303,14 +331,14 @@ public class IndicatorResource implements IndicatorService { @Path(PATH_SUMMARY) @Produces(MediaType.APPLICATION_JSON) @Override - public SummaryDTO getSummary(@QueryParam(value = "indicator") final String indicatorUid, - @QueryParam(value = "period") final String periodCode, - @QueryParam(value = "level") @NonNull final FeatureLevel level, - @QueryParam(value = "id") final String id, - @QueryParam(value = "year") final Integer year) { - checkRequired(indicatorUid, "indicator"); - checkRequired(periodCode, "period"); - checkRequired(year, "year"); + public SummaryDTO getSummary(@QueryParam(value = PARAM_INDICATOR) final String indicatorUid, + @QueryParam(value = PARAM_PERIOD) final String periodCode, + @QueryParam(value = PARAM_LEVEL) @NonNull final FeatureLevel level, + @QueryParam(value = PARAM_ID) final String id, + @QueryParam(value = PARAM_YEAR) final Integer year) { + checkRequired(indicatorUid, PARAM_INDICATOR); + checkRequired(periodCode, PARAM_PERIOD); + checkRequired(year, PARAM_YEAR); final var locale = LocaleUtils.getLocale(httpServletRequest); // HTTP cache headers cacheService.setCacheKey(PATH, PATH_SUMMARY, locale, indicatorUid, periodCode, @@ -419,13 +447,14 @@ public class IndicatorResource implements IndicatorService { @Path(PATH_VALUES) @Produces("application/geo+json") @Override - public FeatureCollection getValues(@QueryParam(value = "indicator") final String indicatorUid, - @QueryParam(value = "period") final String periodCode, @QueryParam(value = "region") final Integer regionId, - @QueryParam(value = "year") final Integer year, - @QueryParam(value = "comparison") final Boolean comparison) { - checkRequired(indicatorUid, "indicator"); - checkRequired(periodCode, "period"); - checkRequired(year, "year"); + public FeatureCollection getValues(@QueryParam(value = PARAM_INDICATOR) final String indicatorUid, + @QueryParam(value = PARAM_PERIOD) final String periodCode, + @QueryParam(value = PARAM_REGION) final Integer regionId, + @QueryParam(value = PARAM_YEAR) final Integer year, + @QueryParam(value = PARAM_COMPARISON) final Boolean comparison) { + checkRequired(indicatorUid, PARAM_INDICATOR); + checkRequired(periodCode, PARAM_PERIOD); + checkRequired(year, PARAM_YEAR); final var locale = LocaleUtils.getLocale(httpServletRequest); cacheService.setCacheKey(PATH, PATH_VALUES, indicatorUid, periodCode, regionId, year, comparison, locale); diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/rs/SurveyFormResource.java b/www-server/src/main/java/fr/agrometinfo/www/server/rs/SurveyFormResource.java index 6be1db7adb1d9500df5ab3a5d3411024a87c21cd..d837927fe30d23cb06329c2d77ffa305c144c2ab 100644 --- a/www-server/src/main/java/fr/agrometinfo/www/server/rs/SurveyFormResource.java +++ b/www-server/src/main/java/fr/agrometinfo/www/server/rs/SurveyFormResource.java @@ -2,7 +2,6 @@ package fr.agrometinfo.www.server.rs; import java.time.LocalDateTime; import java.util.List; -import java.util.stream.Collectors; import fr.agrometinfo.www.server.AgroMetInfoConfiguration; import fr.agrometinfo.www.server.dao.SurveyOptionDao; @@ -41,17 +40,6 @@ import lombok.extern.log4j.Log4j2; @Path(SurveyFormService.PATH) @RequestScoped public class SurveyFormResource implements SurveyFormService { - /** - * Convert {@link SurveyQuestion} entity to dto. - * @param question entity - * @return dto - */ - static SurveyQuestionDTO toDto(final SurveyQuestion question) { - final SurveyQuestionDTO dto = new SurveyQuestionDTO(); - dto.setId(question.getId()); - dto.setDescription(question.getDescription()); - return dto; - } /** * Convert {@link SurveyOption} entity to dto. * @param response entity @@ -65,15 +53,15 @@ public class SurveyFormResource implements SurveyFormService { return dto; } /** - * Convert {@link SurveyQuestionDTO} dto to entity. - * @param dto question - * @return entity + * Convert {@link SurveyQuestion} entity to dto. + * @param question entity + * @return dto */ - private static SurveyQuestion toEntity(final SurveyQuestionDTO dto) { - final SurveyQuestion question = new SurveyQuestion(); - question.setId(dto.getId()); - question.setDescription(dto.getDescription()); - return question; + static SurveyQuestionDTO toDto(final SurveyQuestion question) { + final SurveyQuestionDTO dto = new SurveyQuestionDTO(); + dto.setId(question.getId()); + dto.setDescription(question.getDescription()); + return dto; } /** * Convert {@link SurveyOptionDTO} dto to entity. @@ -87,6 +75,17 @@ public class SurveyFormResource implements SurveyFormService { response.setDescription(dto.getDescription()); return response; } + /** + * Convert {@link SurveyQuestionDTO} dto to entity. + * @param dto question + * @return entity + */ + private static SurveyQuestion toEntity(final SurveyQuestionDTO dto) { + final SurveyQuestion question = new SurveyQuestion(); + question.setId(dto.getId()); + question.setDescription(dto.getDescription()); + return question; + } /** * Application configuration. */ @@ -131,7 +130,7 @@ public class SurveyFormResource implements SurveyFormService { badRequest.getStatusCode(), badRequest.getReasonPhrase(), queryParamName + " parameter is mandatory") - ).build()); + ).build()); } } @@ -140,8 +139,7 @@ public class SurveyFormResource implements SurveyFormService { @Produces(MediaType.APPLICATION_JSON) @Override public List<SurveyOptionDTO> getResponses() { - return responsesDao.findAll().stream() - .map(SurveyFormResource::toDto).collect(Collectors.toList()); + return responsesDao.findAll().stream().map(SurveyFormResource::toDto).toList(); } @POST @@ -165,9 +163,9 @@ public class SurveyFormResource implements SurveyFormService { } final Long qRef = dto.getId(); - List<SurveyOptionDTO> associatedResponses = data.getResponses() + final List<SurveyOptionDTO> associatedResponses = data.getResponses() .stream().filter(f -> f.getQuestion().getId() == qRef) - .collect(Collectors.toList()); + .toList(); for (final SurveyOptionDTO rDto : associatedResponses) { final SurveyOption r = toEntity(rDto); @@ -188,7 +186,7 @@ public class SurveyFormResource implements SurveyFormService { // once everything is inserted, send an email to support try { - this.mailService.send(toMail(data)); + this.mailService.send(toMail(data)); } catch (final AgroMetInfoException e) { LOGGER.info("Failed to send e-mail: {}", ""); } @@ -211,17 +209,20 @@ public class SurveyFormResource implements SurveyFormService { builder.append("Un utilisateur vient de remplir le formulaire d'enquête d'AgroMetInfo à l'instant.\n\n"); builder.append("Il a répondu à ").append(data.getQuestions().size()).append(" question(s) :\n"); - data.getQuestions().forEach((q) -> { + data.getQuestions().forEach(q -> { builder.append("\t• « ").append(q.getDescription()).append(" » :\n"); data.getResponses().stream().filter(f -> f.getQuestion().getId() == q.getId()) - .collect(Collectors.toList()) - .forEach((r) -> builder.append("\t\t- ").append(r.getDescription()).append("\n")); + .forEach(r -> builder.append("\t\t- ") // + .append(r.getDescription()) // + .append('\n')); if (data.getOtherTextMap().containsKey(q.getId()) && data.getOtherTextMap().get(q.getId()) != null) { - builder.append("\t\t- Réponse libre : « ").append(data.getOtherTextMap().get(q.getId())).append(" »\n"); + builder.append("\t\t- Réponse libre : « ") // + .append(data.getOtherTextMap().get(q.getId())) // + .append(" »\n"); } - builder.append("\n"); + builder.append('\n'); }); mail.setContent(builder.toString()); return mail; diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/rs/filter/LogFilter.java b/www-server/src/main/java/fr/agrometinfo/www/server/rs/filter/LogFilter.java index 94c4974d26634d6daf4b65b3b4c86802061e7e4a..33d7d08d1cf427a6746f77548767d84b7eec3a3c 100644 --- a/www-server/src/main/java/fr/agrometinfo/www/server/rs/filter/LogFilter.java +++ b/www-server/src/main/java/fr/agrometinfo/www/server/rs/filter/LogFilter.java @@ -46,10 +46,10 @@ public class LogFilter implements ContainerRequestFilter, ContainerResponseFilte */ private void log(final String context, final UriInfo uriInfo, final MultivaluedMap<String, ?> reqHeaders, final MultivaluedMap<String, ?> resHeaders) { - LOGGER.trace("{}\nPath: {}", context, uriInfo.getPath()); - LOGGER.trace("HTTP headers of the request: {}", reqHeaders); + LOGGER.atTrace().log("{}\nPath: {}", context, uriInfo.getPath()); + LOGGER.atTrace().log("HTTP headers of the request: {}", reqHeaders); if (resHeaders != null) { - LOGGER.trace("HTTP headers of the response: {}", resHeaders); + LOGGER.atTrace().log("HTTP headers of the response: {}", resHeaders); } } } diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/scheduled/BackgroundJobManager.java b/www-server/src/main/java/fr/agrometinfo/www/server/scheduled/BackgroundJobManager.java index 6e3f718acc40af6d3cdb7be37f43e1fe6cecdd7d..a70b4b81f3a1addafcff0bad1a6a168d7e97f37b 100644 --- a/www-server/src/main/java/fr/agrometinfo/www/server/scheduled/BackgroundJobManager.java +++ b/www-server/src/main/java/fr/agrometinfo/www/server/scheduled/BackgroundJobManager.java @@ -1,5 +1,6 @@ package fr.agrometinfo.www.server.scheduled; +import java.math.BigDecimal; import java.time.Duration; import java.time.LocalDate; import java.time.LocalDateTime; @@ -15,7 +16,6 @@ import jakarta.inject.Inject; import jakarta.servlet.ServletContextEvent; import jakarta.servlet.ServletContextListener; import jakarta.servlet.annotation.WebListener; -import java.math.BigDecimal; import lombok.extern.log4j.Log4j2; /** @@ -103,8 +103,8 @@ public class BackgroundJobManager implements ServletContextListener { */ private void scheduleEveryDayAt(final Runnable runnable, final int hour, final int minute, final int second) { final long initDelay = initialDelay(hour, minute, second); - LOGGER.trace("Scheduling {} after {}s at {}:{}:{}", runnable.getClass().getCanonicalName(), initDelay, hour, - minute, second); + LOGGER.atTrace().log("Scheduling {} after {}s at {}:{}:{}", runnable.getClass().getCanonicalName(), initDelay, + hour, minute, second); scheduler.scheduleAtFixedRate(runnable, initDelay, DAY_IN_SECONDS, TimeUnit.SECONDS); } @@ -117,7 +117,7 @@ public class BackgroundJobManager implements ServletContextListener { * period between 2 runs (in seconds) */ private void scheduleEveryPeriod(final Runnable runnable, final int period) { - LOGGER.trace("Scheduling {} after {}s.", runnable.getClass().getCanonicalName(), period); + LOGGER.atTrace().log("Scheduling {} after {}s.", runnable.getClass().getCanonicalName(), period); scheduler.scheduleAtFixedRate(runnable, period, period, TimeUnit.SECONDS); } diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/service/CacheService.java b/www-server/src/main/java/fr/agrometinfo/www/server/service/CacheService.java index 316ef21dc0652abb7ac900e6e9f916a9168564a1..58e95b2101db4dec6e13735d201559d240bc4fa1 100644 --- a/www-server/src/main/java/fr/agrometinfo/www/server/service/CacheService.java +++ b/www-server/src/main/java/fr/agrometinfo/www/server/service/CacheService.java @@ -1,12 +1,11 @@ package fr.agrometinfo.www.server.service; import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; import java.time.LocalDateTime; import java.time.temporal.ChronoUnit; import java.util.Date; @@ -52,7 +51,7 @@ public class CacheService { /** * Number of milliseconds in a day. */ - private static final long MILLISECONDS_IN_A_DAY = 60 * 60 * 24 * 1_000; + private static final long MILLISECONDS_IN_A_DAY = 60 * 60 * 24 * 1_000L; /** * Cache directory path. @@ -62,11 +61,11 @@ public class CacheService { @Named("cacheDirectory") private String cacheDirectory; - /** * Key related to object to cache. */ private String cacheKey; + /** * Date of last modification of indicators values in database. */ @@ -101,7 +100,8 @@ public class CacheService { if (!cacheFile.exists()) { return null; } - try (FileInputStream fis = new FileInputStream(cacheFile); FSTObjectInput in = new FSTObjectInput(fis)) { + try (FSTObjectInput in = new FSTObjectInput( + Files.newInputStream(cacheFile.toPath(), StandardOpenOption.READ))) { return in.readObject(); } catch (final IOException | ClassNotFoundException e) { LOGGER.fatal(e); @@ -119,7 +119,7 @@ public class CacheService { final var cc = new CacheControl(); final LocalDateTime dayAfter = lastModification.plusDays(nbOfDays); final Long maxAge = ChronoUnit.SECONDS.between(LocalDateTime.now(), dayAfter); - // max age = time inn seconds + // max age = time in seconds cc.setMaxAge(maxAge.intValue()); return cc; } @@ -170,8 +170,7 @@ public class CacheService { LOGGER.traceEntry("{}", object); final File cacheFile = getCacheFile(); LOGGER.info("file Path = {}", cacheFile); - try (FileOutputStream fos = new FileOutputStream(cacheFile); - FSTObjectOutput out = FSTCONF.getObjectOutput(fos)) { + try (FSTObjectOutput out = FSTCONF.getObjectOutput(Files.newOutputStream(cacheFile.toPath()))) { out.writeObject(object); out.flush(); } catch (final IOException e) { diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/service/MailServiceImpl.java b/www-server/src/main/java/fr/agrometinfo/www/server/service/MailServiceImpl.java index 230a40cca59be0d53d7058a95df01a456ea49752..bc7264f14006b2e56fce9d39c9b90894b751fac8 100644 --- a/www-server/src/main/java/fr/agrometinfo/www/server/service/MailServiceImpl.java +++ b/www-server/src/main/java/fr/agrometinfo/www/server/service/MailServiceImpl.java @@ -47,6 +47,44 @@ import lombok.Getter; @ApplicationScoped public class MailServiceImpl implements MailService { + /** + * Log4j2 appender to send error logs by e-mail. + */ + private class MailAppender extends AbstractAppender { + + /** + * Logger name. + */ + private static final String NAME = "MailAppender"; + + /** + * Constructor. + * + * @param filter The Filter to associate with the Appender. + * @param layout The layout to use to format the event. + * @param ignoreExceptions If true, exceptions will be logged and suppressed. If + * false errors will be logged and then passed to the + * application. + */ + MailAppender(final Filter filter, final Layout<? extends Serializable> layout, final boolean ignoreExceptions) { + super(NAME, filter, layout, ignoreExceptions, null); + } + + @Override + public void append(final LogEvent event) { + final var mail = new Mail(); + mail.setFromAddress("agrometinfo@inrae.fr"); + mail.setSubject("AgroMetInfo - error log"); + mail.setContent(super.toSerializable(event).toString()); + mail.setToAddresses(List.of(configuration.get(ConfigurationKey.LOG_EMAIL))); + try { + send(mail); + } catch (final AgroMetInfoException e) { + // do nothing + LOGGER.atInfo().log("Cannot send email: {}", e.getMessage()); + } + } + } /** * Keys from messages.properties used to warn about errors. @@ -97,42 +135,15 @@ public class MailServiceImpl implements MailService { } /** - * Log4j2 appender to send error logs by e-mail. + * @param addresses e-mail addresses + * @return string representation of addresses */ - private class MailAppender extends AbstractAppender { - - /** - * Logger name. - */ - private static final String NAME = "MailAppender"; - - /** - * Constructor. - * - * @param filter The Filter to associate with the Appender. - * @param layout The layout to use to format the event. - * @param ignoreExceptions If true, exceptions will be logged and suppressed. If - * false errors will be logged and then passed to the - * application. - */ - MailAppender(final Filter filter, final Layout<? extends Serializable> layout, final boolean ignoreExceptions) { - super(NAME, filter, layout, ignoreExceptions, null); - } - - @Override - public void append(final LogEvent event) { - final var mail = new Mail(); - mail.setFromAddress("agrometinfo@inrae.fr"); - mail.setSubject("AgroMetInfo - error log"); - mail.setContent(super.toSerializable(event).toString()); - mail.setToAddresses(List.of(configuration.get(ConfigurationKey.LOG_EMAIL))); - try { - send(mail); - } catch (final AgroMetInfoException e) { - // do nothing - LOGGER.info("Cannot send email: {}", e.getMessage()); - } + private static String toString(final Address[] addresses) { + final var sj = new StringJoiner(", "); + for (final Address addr : addresses) { + sj.add(addr.toString()); } + return sj.toString(); } /** @@ -215,17 +226,16 @@ public class MailServiceImpl implements MailService { } try { Transport.send(message); - } catch (final MessagingException e) { - if (e instanceof final SendFailedException sfe) { - final Address[] invalid = sfe.getInvalidAddresses(); - if (invalid != null) { - throw new AgroMetInfoException(MailErrorType.SEND_FAILED_INVALID, subject, toString(invalid)); - } - final Address[] validUnsent = sfe.getValidUnsentAddresses(); - if (validUnsent != null) { - throw new AgroMetInfoException(MailErrorType.SEND_FAILED_VALID, subject, toString(validUnsent)); - } + } catch (final SendFailedException sfe) { + final Address[] invalid = sfe.getInvalidAddresses(); + if (invalid != null) { + throw new AgroMetInfoException(MailErrorType.SEND_FAILED_INVALID, subject, toString(invalid)); } + final Address[] validUnsent = sfe.getValidUnsentAddresses(); + if (validUnsent != null) { + throw new AgroMetInfoException(MailErrorType.SEND_FAILED_VALID, subject, toString(validUnsent)); + } + } catch (final MessagingException e) { throw new AgroMetInfoException(MailErrorType.SEND_FAILED, subject, recipients, e.getMessage()); } } @@ -251,7 +261,7 @@ public class MailServiceImpl implements MailService { to.add(new InternetAddress(addr)); } message.setRecipients(Message.RecipientType.TO, to.toArray(InternetAddress[]::new)); - if ("prod".equals(environment)) { + if (configuration.isProdEnvironment()) { message.setSubject("AgroMetInfo : " + mail.getSubject()); } else { message.setSubject("AgroMetInfo " + environment + " : " + mail.getSubject()); @@ -265,16 +275,4 @@ public class MailServiceImpl implements MailService { } } - /** - * @param addresses e-mail addresses - * @return string representation of addresses - */ - private String toString(final Address[] addresses) { - final var sj = new StringJoiner(", "); - for (final Address addr : addresses) { - sj.add(addr.toString()); - } - return sj.toString(); - } - } diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/util/AppVersion.java b/www-server/src/main/java/fr/agrometinfo/www/server/util/AppVersion.java index 7c22f781de48d1b8eb4c29f73b23917950a2068f..4928a0b60fc3960d6e293da69635ddb25a7e0e69 100644 --- a/www-server/src/main/java/fr/agrometinfo/www/server/util/AppVersion.java +++ b/www-server/src/main/java/fr/agrometinfo/www/server/util/AppVersion.java @@ -23,7 +23,7 @@ public final class AppVersion { * @return Date of application build */ public static Date getBuildDate() { - final var sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + final var sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.ROOT); try { return sdf.parse(getString("build.date")); } catch (final ParseException e) { diff --git a/www-server/src/test/java/fr/agrometinfo/www/server/I18nTest.java b/www-server/src/test/java/fr/agrometinfo/www/server/I18nTest.java index 2f94f124e22f1c80e049d595632339b0ba1ebfa6..185fa50a5abe4764a989fe6a69a385c14788fc53 100644 --- a/www-server/src/test/java/fr/agrometinfo/www/server/I18nTest.java +++ b/www-server/src/test/java/fr/agrometinfo/www/server/I18nTest.java @@ -24,6 +24,8 @@ import fr.agrometinfo.www.server.I18n.Operator; class I18nTest { + private static final String KEY_CATS = "cats"; + private static final String KEY_COMPARISON = "comparison"; /** * Key for plural messages to test. */ @@ -45,15 +47,15 @@ class I18nTest { Arguments.of(3, KEY, "Il y a 3 produits dans votre panier, ce qui est peu."), // Arguments.of(42, KEY, "Il y a 42 produits dans votre panier, ce qui est un nombre spécial."), // Arguments.of(101, KEY, "Il y a 101 produits dans votre panier, ce qui est beaucoup."), // -Arguments.of(1_314, KEY, "Il y a 1 314 produits dans votre panier, ce qui est beaucoup."), // - Arguments.of(1, "cats", "Un chat"), // + Arguments.of(1_314, KEY, "Il y a 1 314 produits dans votre panier, ce qui est beaucoup."), // + Arguments.of(1, KEY_CATS, "Un chat"), // // Variation is not present, default value. - Arguments.of(2, "cats", "2 chats"), // - Arguments.of(1, "comparison", "Valeur == 1"), // - Arguments.of(10, "comparison", "Valeur >= 10"), // - Arguments.of(11, "comparison", "Valeur > 10"), // - Arguments.of(-10, "comparison", "Valeur <= -10"), // - Arguments.of(-101, "comparison", "Valeur < -100") // + Arguments.of(2, KEY_CATS, "2 chats"), // + Arguments.of(1, KEY_COMPARISON, "Valeur == 1"), // + Arguments.of(10, KEY_COMPARISON, "Valeur >= 10"), // + Arguments.of(11, KEY_COMPARISON, "Valeur > 10"), // + Arguments.of(-10, KEY_COMPARISON, "Valeur <= -10"), // + Arguments.of(-101, KEY_COMPARISON, "Valeur < -100") // ); } @@ -65,14 +67,14 @@ Arguments.of(1_314, KEY, "Il y a 1 314 produits dans votre panier, ce qui est @Test void contructorWithResourceBundle() { final var bundle = ResourceBundle.getBundle(NAME, Locale.FRENCH); - final var res = new I18n(bundle); + final var i18n = new I18n(bundle); String actual; String expected; - actual = res.get("error.title"); + actual = i18n.get("error.title"); expected = "Erreur"; assertEquals(expected, actual); - actual = res.get("not.translated"); + actual = i18n.get("not.translated"); expected = "This message is not translated."; assertEquals(expected, actual); } @@ -154,8 +156,8 @@ Arguments.of(1_314, KEY, "Il y a 1 314 produits dans votre panier, ce qui est operators.put("<0", I18n.Operator.CINF); operators.put("<=0", I18n.Operator.BINFEQ); operators.forEach((comparison, expected) -> { - final Optional<Operator> res = I18n.Operator.extract(comparison); - final I18n.Operator actual = res.orElse(null); + final Optional<Operator> optional = I18n.Operator.extract(comparison); + final I18n.Operator actual = optional.orElse(null); assertEquals(expected, actual, expected + " must be extracted from " + comparison); }); } diff --git a/www-server/src/test/java/fr/agrometinfo/www/server/dao/CellDaoHibernateTest.java b/www-server/src/test/java/fr/agrometinfo/www/server/dao/CellDaoHibernateTest.java index ac37e1bbaa773b334af9a844debd30eedac61921..3a35e2f9b860c6db1a65df0117f737baffe22d79 100644 --- a/www-server/src/test/java/fr/agrometinfo/www/server/dao/CellDaoHibernateTest.java +++ b/www-server/src/test/java/fr/agrometinfo/www/server/dao/CellDaoHibernateTest.java @@ -11,7 +11,7 @@ public class CellDaoHibernateTest { /** * Total number of cells in the table. */ - public static final long COUNT = 8_602; + public static final long EXPECTED_COUNT = 8_602; /** * DAO to test. */ @@ -23,7 +23,7 @@ public class CellDaoHibernateTest { @Test void count() { final long actual = dao.count(); - assertEquals(COUNT, actual); + assertEquals(EXPECTED_COUNT, actual); } /** @@ -32,6 +32,6 @@ public class CellDaoHibernateTest { @Test void findAll() { final var actual = dao.findAll(); - assertEquals(COUNT, actual.size()); + assertEquals(EXPECTED_COUNT, actual.size()); } } diff --git a/www-server/src/test/java/fr/agrometinfo/www/server/dao/DaoHibernateTest.java b/www-server/src/test/java/fr/agrometinfo/www/server/dao/DaoHibernateTest.java new file mode 100644 index 0000000000000000000000000000000000000000..48e5e13c030d0980ac629a0d776cbc3edc8bad1f --- /dev/null +++ b/www-server/src/test/java/fr/agrometinfo/www/server/dao/DaoHibernateTest.java @@ -0,0 +1,24 @@ +package fr.agrometinfo.www.server.dao; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +import fr.agrometinfo.www.server.model.Cell; + +/** + * Test internal or common methods of Dao Hibernate implementation. + */ +class DaoHibernateTest { + + /** + * DAO to test. + */ + private final DaoHibernate<Cell> dao = new DaoHibernate<Cell>(Cell.class); + + @Test + void getDriver() { + final String actual = dao.getDriver(); + assertEquals("H2 JDBC Driver", actual); + } +} diff --git a/www-server/src/test/java/fr/agrometinfo/www/server/dao/PraDaoHibernateTest.java b/www-server/src/test/java/fr/agrometinfo/www/server/dao/PraDaoHibernateTest.java index ca1875e3d9e0a1e02a615ece060d48f1082ec89d..925b519c46e49438005a576311b5641c0e71f0a7 100644 --- a/www-server/src/test/java/fr/agrometinfo/www/server/dao/PraDaoHibernateTest.java +++ b/www-server/src/test/java/fr/agrometinfo/www/server/dao/PraDaoHibernateTest.java @@ -12,7 +12,7 @@ public class PraDaoHibernateTest { /** * Total number of PRAs in the table. */ - public static final long COUNT = 714; + public static final long EXPECTED_COUNT = 714; /** * DAO to test. */ @@ -24,7 +24,7 @@ public class PraDaoHibernateTest { @Test void count() { final var actual = dao.count(); - assertEquals(COUNT, actual); + assertEquals(EXPECTED_COUNT, actual); } /** @@ -33,7 +33,7 @@ public class PraDaoHibernateTest { @Test void findAll() { final var actual = dao.findAll(); - assertEquals(COUNT, actual.size()); + assertEquals(EXPECTED_COUNT, actual.size()); final var first = actual.get(0); assertNotNull(first.getCoordinates()); final int expected = 58; diff --git a/www-server/src/test/java/fr/agrometinfo/www/server/rs/GeometryResourceTest.java b/www-server/src/test/java/fr/agrometinfo/www/server/rs/GeometryResourceTest.java index 59b69f6df7df1f21734b455df8b1a4bd3c33b9ee..8c87f72378b3bca7102662f5eac810fac96478a2 100644 --- a/www-server/src/test/java/fr/agrometinfo/www/server/rs/GeometryResourceTest.java +++ b/www-server/src/test/java/fr/agrometinfo/www/server/rs/GeometryResourceTest.java @@ -67,7 +67,7 @@ class GeometryResourceTest extends JerseyTest { .request()// .get(FeatureCollection.class); assertNotNull(actual); - final Integer expected = (int) PraDaoHibernateTest.COUNT; + final Integer expected = (int) PraDaoHibernateTest.EXPECTED_COUNT; assertEquals(expected, actual.getFeatures().size()); } } diff --git a/www-server/src/test/java/fr/agrometinfo/www/server/rs/IndicatorResourceTest.java b/www-server/src/test/java/fr/agrometinfo/www/server/rs/IndicatorResourceTest.java index 23a483341aed631a6806c05f2b5860879de74214..abeb82bca2ac14e6eec312454bd58c2b56c55f78 100644 --- a/www-server/src/test/java/fr/agrometinfo/www/server/rs/IndicatorResourceTest.java +++ b/www-server/src/test/java/fr/agrometinfo/www/server/rs/IndicatorResourceTest.java @@ -42,6 +42,14 @@ import jakarta.ws.rs.core.Application; * @author Olivier Maury */ class IndicatorResourceTest extends JerseyTest { + private static final String PARAM_LEVEL = "level"; + + private static final String PARAM_PERIOD = "period"; + + private static final String PARAM_INDICATOR = "indicator"; + + private static final String PARAM_YEAR = "year"; + /** * Path separator. */ @@ -96,11 +104,11 @@ class IndicatorResourceTest extends JerseyTest { @Test void getSummary() { final var actual = target(IndicatorService.PATH + SEP + IndicatorService.PATH_SUMMARY)// - .queryParam("indicator", "rainsum") // - .queryParam("period", "year") // - .queryParam("level", "PRA") // + .queryParam(PARAM_INDICATOR, "rainsum") // + .queryParam(PARAM_PERIOD, "year") // + .queryParam(PARAM_LEVEL, "PRA") // .queryParam("id", "59325") // - .queryParam("year", "2023") // + .queryParam(PARAM_YEAR, "2023") // .request()// .get(SummaryDTO.class); assertNotNull(actual, "response must not be null!"); @@ -115,10 +123,10 @@ class IndicatorResourceTest extends JerseyTest { @Test void getValues() { final var actual = target(IndicatorService.PATH + SEP + IndicatorService.PATH_VALUES)// - .queryParam("indicator", "rainsum") // - .queryParam("period", "year") // + .queryParam(PARAM_INDICATOR, "rainsum") // + .queryParam(PARAM_PERIOD, "year") // .queryParam("compare", "false") // - .queryParam("year", "2023") // + .queryParam(PARAM_YEAR, "2023") // .request()// .get(FeatureCollection.class); assertNotNull(actual); diff --git a/www-server/src/test/java/fr/agrometinfo/www/server/rs/SurveyFormResourceTest.java b/www-server/src/test/java/fr/agrometinfo/www/server/rs/SurveyFormResourceTest.java index c20c4e2f7274bb03b56abbc3db47140a6d865c71..380f18006ce9575c261c7d49a1dfefba05dd72cf 100644 --- a/www-server/src/test/java/fr/agrometinfo/www/server/rs/SurveyFormResourceTest.java +++ b/www-server/src/test/java/fr/agrometinfo/www/server/rs/SurveyFormResourceTest.java @@ -54,7 +54,7 @@ import lombok.extern.log4j.Log4j2; * @author jdecome */ @Log4j2 -public class SurveyFormResourceTest extends JerseyTest { +class SurveyFormResourceTest extends JerseyTest { /** * Mock class for mail service. * @@ -104,7 +104,7 @@ public class SurveyFormResourceTest extends JerseyTest { } /** Testing response retrieval from webservice. */ @Test - public void getResponses() { + void getResponses() { try { // get all questions from DAO for verification. final List<SurveyQuestion> questions = questionsDao.findAll(); @@ -123,7 +123,7 @@ public class SurveyFormResourceTest extends JerseyTest { assertTrue(questions.contains(r.getQuestion()), "Question of the answer « " + r.getDescription() + " » is not contained in the list of questions"); } } catch (final JsonProcessingException e) { - log.info(e.getMessage()); + LOGGER.atInfo().log(e.getMessage()); } } /** @@ -151,7 +151,7 @@ public class SurveyFormResourceTest extends JerseyTest { /** Test by setting answers for questions. */ @Test - public void testWithJsonResponses() { + void testWithJsonResponses() { this.resetUserResponses(); final List<SurveyQuestion> questionsList = questionsDao.findAll(); final List<SurveyQuestionDTO> questionsDTO = questionsList.stream().map(SurveyFormResource::toDto).toList(); @@ -175,21 +175,21 @@ public class SurveyFormResourceTest extends JerseyTest { assertEquals(data.getQuestions(), questionsDTO, "List of questions in LoginFormData object does not match with original list"); assertEquals(data.getResponses(), responsesDTO, "List of responses in LoginFormData object does not match with the original list"); assertEquals(data.getOtherTextMap(), otherTextMap, "List of free responses in LoginFormData object does not match with the original list"); - var ret = target(SurveyFormService.PATH + SEP + SurveyFormService.PATH_INSERT_RESPONSE) + final var ret = target(SurveyFormService.PATH + SEP + SurveyFormService.PATH_INSERT_RESPONSE) .request(MediaType.APPLICATION_JSON).post(Entity.entity(data, MediaType.APPLICATION_JSON)); assertEquals(ret.getStatus(), jakarta.ws.rs.core.Response.Status.OK.getStatusCode(), "The insert entry point should return status code 200 (OK)"); // Retrieves all answers for all questions - List<UserResponse> list = new ArrayList<>(); + final List<UserResponse> list = new ArrayList<>(); for (final SurveyQuestion q : questionsDao.findAll()) { final List<UserResponse> responsesByQuestion = userResponsesDao.findAllByQuestion(q.getId()); assertNotNull(responsesByQuestion, "The user's answer list for the question « " + q.getId() + " » must not be null"); - responsesByQuestion.forEach((ur) -> { + responsesByQuestion.forEach(ur -> { assertEquals(ur.getQuestion(), q, "The question in the answer does not match the original question « " + q.getDescription() + " »"); }); list.addAll(responsesByQuestion); } // Test on all answers - list.forEach((ur) -> { + list.forEach(ur -> { assertNotNull(ur.getDateTime(), "The response timestamp must not be null"); assertTrue(ur.getId() > 0, "Reference of the user response must be greater than 0"); if (ur.getOption() != null) { @@ -201,20 +201,23 @@ public class SurveyFormResourceTest extends JerseyTest { } }); // Check if all records has the same datetime - assertEquals(list.stream().map((u) -> u.getDateTime()).distinct().count(), 1, "Not all answers have the same datetime"); - final LocalDateTime datetimeResponse = list.stream().map((u) -> u.getDateTime()).distinct().findFirst().get(); + assertEquals(1, list.stream().map(UserResponse::getDateTime).distinct().count(), + "Not all answers have the same datetime"); + final LocalDateTime datetimeResponse = list.stream().map(UserResponse::getDateTime).distinct().findFirst() + .get(); assertNotNull(datetimeResponse, "The datetime read in the responses is null"); // in this case, email address has inserted final List<UserEmail> emails = this.userEmailDao.getEmailAddressList(); assertNotNull(emails, "The email list is not initialized (= null)"); assertFalse(emails.isEmpty(), "Email list is empty"); - emails.forEach((e) -> { - assertTrue((e.getId() > 0), "The object id must be greater than 0"); + emails.forEach(e -> { + assertTrue(e.getId() > 0, "The object id must be greater than 0"); assertNotNull(e.getDatetime(), "The timestamp of the question must not be null"); assertNotNull(e.getEmail(), "The email address contained in the subject must not be null"); }); - assertEquals(emails.stream().map((e) -> e.getDatetime()).distinct().count(), 1, "Not all email addresses have the same datetime"); - final LocalDateTime datetimeEmail = emails.stream().map((e) -> e.getDatetime()).distinct().findFirst().get(); + assertEquals(1, emails.stream().map(e -> e.getDatetime()).distinct().count(), + "Not all email addresses have the same datetime"); + final LocalDateTime datetimeEmail = emails.stream().map(e -> e.getDatetime()).distinct().findFirst().get(); assertNotNull(datetimeEmail, "The datetime read in emails is null"); assertEquals(datetimeResponse, datetimeEmail, "The two datetimes are not identical"); // Check mail sended to support, when user filled survey form diff --git a/www-server/src/test/java/lombok.config b/www-server/src/test/java/lombok.config new file mode 100644 index 0000000000000000000000000000000000000000..aa03b2b4b29cc30538b9ff4aedb0ad557b5e0156 --- /dev/null +++ b/www-server/src/test/java/lombok.config @@ -0,0 +1,2 @@ +# configuration directives for Lombok. These apply to all source files in this directory and all child directories. +lombok.log.fieldName = LOGGER \ No newline at end of file diff --git a/www-server/src/test/resources/log4j2.xml b/www-server/src/test/resources/log4j2.xml index b72949cd6ad91cb5444cd5554c1e7dae4ac2d122..b83f6e6144e5fe7e769a34e80326bb50bed9bd4d 100644 --- a/www-server/src/test/resources/log4j2.xml +++ b/www-server/src/test/resources/log4j2.xml @@ -4,7 +4,7 @@ <Appenders> <Console name="console" target="SYSTEM_OUT"> <PatternLayout> - <pattern>dev-cas | %d | %-5p | %c#%M() | %L - %m%n</pattern> + <pattern>%d | %-5p | %c#%M() | %L - %m%n</pattern> </PatternLayout> </Console> </Appenders> diff --git a/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/ChoiceDTO.java b/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/ChoiceDTO.java index 40cf0a0d479fa2f9a6b17b14d95446f4982c1c10..e0a155b1c19f640d19b570890d9c79f3a198f602 100644 --- a/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/ChoiceDTO.java +++ b/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/ChoiceDTO.java @@ -1,12 +1,13 @@ package fr.agrometinfo.www.shared.dto; +import static java.lang.Boolean.FALSE; +import static java.lang.Boolean.TRUE; + import java.io.Serializable; import org.dominokit.jackson.annotation.JSONMapper; import com.fasterxml.jackson.annotation.JsonIgnore; -import static java.lang.Boolean.FALSE; -import static java.lang.Boolean.TRUE; /** * Choice from user to get indicators. @@ -96,8 +97,8 @@ public final class ChoiceDTO implements Serializable { */ @JsonIgnore public boolean isValid() { - return period != null && !period.trim().isEmpty() // - && indicator != null && !indicator.trim().isEmpty() // + return period != null && !period.isBlank() // + && indicator != null && !indicator.isBlank() // && year != null; } diff --git a/www-shared/src/main/java/fr/agrometinfo/www/shared/service/IndicatorService.java b/www-shared/src/main/java/fr/agrometinfo/www/shared/service/IndicatorService.java index 01525cecef5ff72048fe73ec0f0380de823ab09b..3ff1658ae7365b9284c450758819e10205052a27 100644 --- a/www-shared/src/main/java/fr/agrometinfo/www/shared/service/IndicatorService.java +++ b/www-shared/src/main/java/fr/agrometinfo/www/shared/service/IndicatorService.java @@ -22,6 +22,10 @@ import jakarta.ws.rs.QueryParam; @RequestFactory @Path(IndicatorService.PATH) public interface IndicatorService { + /** + * Same string for all. + */ + String PARAM_YEAR = "year"; /** * Service base path. */ @@ -66,7 +70,7 @@ public interface IndicatorService { */ @GET @Path(PATH_LIST) - List<PeriodDTO> getPeriods(@QueryParam(value = "year") Integer year); + List<PeriodDTO> getPeriods(@QueryParam(value = PARAM_YEAR) Integer year); /** * @param year the year of indicator values @@ -74,7 +78,7 @@ public interface IndicatorService { */ @GET @Path(PATH_REGIONS) - Map<String, String> getRegions(@QueryParam(value = "year") Integer year); + Map<String, String> getRegions(@QueryParam(value = PARAM_YEAR) Integer year); /** * @param indicator indicator code @@ -88,7 +92,7 @@ public interface IndicatorService { @Path(PATH_SUMMARY) SummaryDTO getSummary(@QueryParam(value = "indicator") String indicator, @QueryParam(value = "period") String period, @QueryParam(value = "level") FeatureLevel level, - @QueryParam(value = "id") String id, @QueryParam(value = "year") Integer year); + @QueryParam(value = "id") String id, @QueryParam(value = PARAM_YEAR) Integer year); /** * @param indicator indicator code @@ -102,7 +106,7 @@ public interface IndicatorService { @Path(PATH_VALUES) FeatureCollection getValues(@QueryParam(value = "indicator") String indicator, @QueryParam(value = "period") String period, @QueryParam(value = "region") Integer region, - @QueryParam(value = "year") Integer year, @QueryParam(value = "comparison") Boolean comparison); + @QueryParam(value = PARAM_YEAR) Integer year, @QueryParam(value = "comparison") Boolean comparison); /** * @return list of computed years. diff --git a/www-shared/src/main/java/org/geojson/Feature.java b/www-shared/src/main/java/org/geojson/Feature.java index 30dbbef3f20de8f9c9d39f7b8f8d9825f2e168dd..70beafb51252c087534a4c133ad19906d8b708ee 100644 --- a/www-shared/src/main/java/org/geojson/Feature.java +++ b/www-shared/src/main/java/org/geojson/Feature.java @@ -17,7 +17,6 @@ public final class Feature extends GeoJsonObject { */ private static final long serialVersionUID = 5820780438006585201L; - /** * Geometric attribute of the feature. */ @@ -27,6 +26,7 @@ public final class Feature extends GeoJsonObject { * A unique identifier for the feature, never <code>null</code>. */ private String id; + /** * Associated properties. */ diff --git a/www-shared/src/main/java/org/geojson/MultiPolygon.java b/www-shared/src/main/java/org/geojson/MultiPolygon.java index 2f9dca456643e39ccbbeaed2822c84ac88e1ad19..456b5792b51ab56b47e61102125872815cbb1d9c 100644 --- a/www-shared/src/main/java/org/geojson/MultiPolygon.java +++ b/www-shared/src/main/java/org/geojson/MultiPolygon.java @@ -11,7 +11,7 @@ import org.dominokit.jackson.annotation.JSONMapper; * @author Olivier Maury */ @JSONMapper -public class MultiPolygon extends GeoJsonObject { +public final class MultiPolygon extends GeoJsonObject { /** * GeoJSON object type.