From 4ee50e257b32f6ec0f72907305d1f2b1212808a4 Mon Sep 17 00:00:00 2001
From: Joaquín Reñé <jrene@curisit.net>
Date: Fri, 27 Mar 2026 15:07:12 +0000
Subject: [PATCH] #4479 - upgrade SecurisServer to Java 21
---
securis/src/main/java/net/curisit/securis/services/exception/CurisException.java | 86 +++
securis/pom.xml | 29
securis/src/main/java/net/curisit/securis/DefaultExceptionHandler.java | 37 +
securis/src/main/java/net/curisit/securis/RestServicesApplication.java | 1
securis/src/main/java/net/curisit/securis/ioc/RequestsInterceptor.java | 202 +++++++-
securis/src/main/webapp/WEB-INF/web.xml | 83 +-
securis/src/main/java/net/curisit/securis/utils/Utils.java | 719 +++++++++++++++++++++++++++++++
securis/src/main/java/net/curisit/securis/services/BasicServices.java | 4
securis/src/main/java/net/curisit/securis/AppVersion.java | 158 ++++++
securis/src/main/java/net/curisit/securis/services/exception/SeCurisServiceException.java | 2
10 files changed, 1,222 insertions(+), 99 deletions(-)
diff --git a/securis/pom.xml b/securis/pom.xml
index eb22eda..4d4555d 100644
--- a/securis/pom.xml
+++ b/securis/pom.xml
@@ -5,7 +5,8 @@
<modelVersion>4.0.0</modelVersion>
<groupId>net.curisit</groupId>
<artifactId>securis-server</artifactId>
- <version>2.0.2</version>
+ <version>3.0.0</version>
+ <packaging>war</packaging>
<name>SeCuris-Server</name>
<properties>
@@ -14,11 +15,13 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
+
<resteasy.version>6.2.4.Final</resteasy.version>
<hibernate.version>5.6.15.Final</hibernate.version>
<jakarta.persistence.version>3.1.0</jakarta.persistence.version>
- <jakarta.servlet.version>6.0.0</jakarta.servlet.version>
+ <jakarta.servlet.version>6.1.0</jakarta.servlet.version>
<jakarta.cdi.version>4.0.1</jakarta.cdi.version>
+ <beanutils.version>1.9.4</beanutils.version>
<log4j.version>2.18.0</log4j.version>
</properties>
@@ -36,12 +39,7 @@
<artifactId>resteasy-core</artifactId>
<version>${resteasy.version}</version>
</dependency>
- <dependency>
- <groupId>org.jboss.resteasy</groupId>
- <artifactId>resteasy-servlet-initializer</artifactId>
- <version>${resteasy.version}</version>
- </dependency>
- <dependency>
+ <dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-multipart-provider</artifactId>
<version>${resteasy.version}</version>
@@ -83,6 +81,13 @@
</dependency>
<!-- Hibernate 5 compatible con Jakarta Persistence -->
+ <!--
+ <dependency>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-core</artifactId>
+ <version>${hibernate.version}</version>
+ </dependency>
+ -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
@@ -111,6 +116,14 @@
<artifactId>guice</artifactId>
<version>5.1.0</version>
</dependency>
+
+ <!-- Bean utils -->
+ <dependency>
+ <groupId>commons-beanutils</groupId>
+ <artifactId>commons-beanutils</artifactId>
+ <version>${beanutils.version}</version>
+ </dependency>
+
</dependencies>
diff --git a/securis/src/main/java/net/curisit/securis/AppVersion.java b/securis/src/main/java/net/curisit/securis/AppVersion.java
new file mode 100644
index 0000000..0e27181
--- /dev/null
+++ b/securis/src/main/java/net/curisit/securis/AppVersion.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright @ 2013 CurisTEC, S.A.S. All Rights Reserved.
+ */
+package net.curisit.securis;
+
+import java.io.IOException;
+import java.text.MessageFormat;
+import java.util.Properties;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+/**
+ * AppVersion
+ * <p>
+ * This class allows to read a version.properties file with information about
+ * application version This properties file is created automatically during
+ * compilation process. The source of this information is the version string
+ * inside pom.xml file. Format version has this format:
+ * {majorVersion}.{minorVersion}.{incrementalVersion}[-{qualifier}]
+ *
+ * @author cesar
+ * Last reviewed by JRA on Oct 5, 2025.
+ */
+public class AppVersion {
+
+ private static AppVersion instance;
+ private Properties prop;
+
+ private static final String PROPERTIES_FILE_NAME = "/version.properties";
+ private static final Logger LOG = LogManager.getLogger(AppVersion.class);
+
+ /**
+ * AppVersion<p>
+ * Constructor
+ */
+ private AppVersion() {
+ prop = new Properties();
+ try {
+ prop.load(getClass().getResourceAsStream(PROPERTIES_FILE_NAME));
+ } catch (IOException e) {
+ LOG.error("Version file is missing", e);
+ }
+ }
+
+ /**
+ * getInstance<p>
+ * Get unique instance
+ *
+ * @return instance
+ */
+ public synchronized static AppVersion getInstance() {
+ if (instance == null) {
+ instance = new AppVersion();
+ }
+ return instance;
+ }
+
+ /**
+ * getMajorVersion<p>
+ * Returns the major version
+ *
+ * @return majorVersion
+ */
+ public Integer getMajorVersion() {
+ return getParamAsInt(Keys.MAJOR_VERSION, -1);
+ }
+
+ /**
+ * getMinorVersion<p>
+ * Return the minor version
+ *
+ * @return minorVersion
+ */
+ public Integer getMinorVersion() {
+ return getParamAsInt(Keys.MINOR_VERSION, -1);
+ }
+
+ /**
+ * getIncrementalVersion<p>
+ * Returns the incremental version
+ *
+ * @return incrementalVersion
+ */
+ public Integer getIncrementalVersion() {
+ return getParamAsInt(Keys.INCREMENTAL_VERSION, -1);
+ }
+
+ /**
+ * getQualifier<p>
+ * Returns qualifier if it exists
+ *
+ * @return qualifier
+ */
+ public String getQualifier() {
+ return getParam(Keys.QUALIFIER);
+ }
+
+ /**
+ * getCompleteVersion<p>
+ * Return complete version
+ *
+ * @return completeVersion
+ */
+ public String getCompleteVersion() {
+ String strVersion = MessageFormat.format("{0}.{1}.{2}", getMajorVersion(), getMinorVersion(), getIncrementalVersion());
+ if (getQualifier() != null && !getQualifier().isEmpty())
+ strVersion = strVersion + "-" + getQualifier();
+ return strVersion;
+ }
+
+ /*********************** Private methods *********************/
+
+ /**
+ * getParam<p>
+ * Get the parameter associated with the key
+ *
+ * @param key
+ * @return param
+ */
+ private String getParam(String key) {
+ return prop.getProperty(key, null);
+ }
+
+ /**
+ * getParamAsInt<p>
+ * Get the parameter as integer
+ *
+ * @param key
+ * @param defaulValue
+ * @return paramAsInt
+ */
+ private Integer getParamAsInt(String key, Integer defaulValue) {
+ String value = getParam(key);
+ try {
+ if (value == null) {
+ LOG.error("Wrong version field");
+ return defaulValue;
+ } else
+ return Integer.parseInt(value);
+ } catch (NumberFormatException e) {
+ LOG.error("Wrong version field");
+ return defaulValue;
+ }
+ }
+
+ /**
+ * Keys<p>
+ * Application version keys
+ */
+ private static class Keys {
+ public static final String MAJOR_VERSION = "majorVersion";
+ public static final String MINOR_VERSION = "minorVersion";
+ public static final String INCREMENTAL_VERSION = "incrementalVersion";
+ public static final String QUALIFIER = "qualifier";
+ }
+
+}
diff --git a/securis/src/main/java/net/curisit/securis/DefaultExceptionHandler.java b/securis/src/main/java/net/curisit/securis/DefaultExceptionHandler.java
index 6605752..014103d 100644
--- a/securis/src/main/java/net/curisit/securis/DefaultExceptionHandler.java
+++ b/securis/src/main/java/net/curisit/securis/DefaultExceptionHandler.java
@@ -67,8 +67,6 @@
HttpServletRequest request;
@Context
SecurityContext bsc;
- @Context
- EntityManager em;
/**
* toResponse
@@ -81,20 +79,42 @@
releaseEntityManager();
if (e instanceof ForbiddenException) {
LOG.warn("ForbiddenException: {}", e.toString());
- return Response.status(Status.UNAUTHORIZED).header(ERROR_CODE_MESSAGE_HEADER, ErrorCodes.INVALID_CREDENTIALS)
- .header(ERROR_MESSAGE_HEADER, "Unathorized access to the application").type(MediaType.APPLICATION_JSON).build();
+ return Response.status(Status.UNAUTHORIZED)
+ .header(ERROR_CODE_MESSAGE_HEADER, ErrorCodes.INVALID_CREDENTIALS)
+ .header(ERROR_MESSAGE_HEADER, "Unathorized access to the application")
+ .type(MediaType.APPLICATION_JSON)
+ .build();
}
if (e instanceof SeCurisServiceException) {
LOG.warn("SeCurisServiceException: {}", e.toString());
- return Response.status(DEFAULT_APP_ERROR_STATUS_CODE).header(ERROR_CODE_MESSAGE_HEADER, ((SeCurisServiceException) e).getStatus())
- .header(ERROR_MESSAGE_HEADER, e.getMessage()).type(MediaType.APPLICATION_JSON).build();
+ return Response.status(DEFAULT_APP_ERROR_STATUS_CODE)
+ .header(ERROR_CODE_MESSAGE_HEADER, ((SeCurisServiceException) e).getStatus())
+ .header(ERROR_MESSAGE_HEADER, e.getMessage())
+ .type(MediaType.APPLICATION_JSON)
+ .build();
}
+ String path = request != null ? request.getPathInfo() : null;
+ Object user = (bsc != null && bsc.getUserPrincipal() != null) ? bsc.getUserPrincipal() : null;
+ String host = request != null ? request.getRemoteHost() : null;
+ String ua = request != null ? request.getHeader("User-Agent") : null;
+ String url = request != null ? String.valueOf(request.getRequestURL()) : null;
+
+ LOG.error("Unexpected error accessing to '{}' by user: {}", path, user);
+ LOG.error("Request sent from {}, with User-Agent: {}", host, ua);
+ LOG.error("Request url: {}", url, e);
+
+ /**
LOG.error("Unexpected error accesing to '{}' by user: {}", request.getPathInfo(), bsc.getUserPrincipal());
LOG.error("Request sent from {}, with User-Agent: {}", request.getRemoteHost(), request.getHeader("User-Agent"));
LOG.error("Request url: " + request.getRequestURL(), e);
- return Response.serverError().header(ERROR_MESSAGE_HEADER, "Unexpected error: " + e.toString()).type(MediaType.APPLICATION_JSON).build();
+ */
+
+ return Response.serverError()
+ .header(ERROR_MESSAGE_HEADER, "Unexpected error: " + e.toString())
+ .type(MediaType.APPLICATION_JSON)
+ .build();
}
/**
@@ -103,6 +123,8 @@
* Best-effort cleanup: rollback active transaction (if joined) and close the {@link EntityManager}.
*/
private void releaseEntityManager() {
+
+ /**
try {
if (em != null && em.isOpen()) {
LOG.debug("CLOSING EM: {}, trans: {}", em, em.isJoinedToTransaction());
@@ -116,5 +138,6 @@
ex.printStackTrace();
LOG.error("Error closing EM: {}, {}", em, ex);
}
+ */
}
}
diff --git a/securis/src/main/java/net/curisit/securis/RestServicesApplication.java b/securis/src/main/java/net/curisit/securis/RestServicesApplication.java
index 9c9f1e7..086e4e9 100644
--- a/securis/src/main/java/net/curisit/securis/RestServicesApplication.java
+++ b/securis/src/main/java/net/curisit/securis/RestServicesApplication.java
@@ -31,7 +31,6 @@
* @author JRA
* Last reviewed by JRA on Oct 5, 2025.
*/
-@ApplicationPath("/")
public class RestServicesApplication extends Application {
private static final Logger LOG = LogManager.getLogger(RestServicesApplication.class);
diff --git a/securis/src/main/java/net/curisit/securis/ioc/RequestsInterceptor.java b/securis/src/main/java/net/curisit/securis/ioc/RequestsInterceptor.java
index 2417954..dcc89b9 100644
--- a/securis/src/main/java/net/curisit/securis/ioc/RequestsInterceptor.java
+++ b/securis/src/main/java/net/curisit/securis/ioc/RequestsInterceptor.java
@@ -81,18 +81,67 @@
*/
@Override
public void filter(ContainerRequestContext requestContext) throws IOException {
- EntityManager em = emProvider.getEntityManager();
- LOG.debug("GETTING EM: {}", em);
+
+ Method method = resourceInfo != null ? resourceInfo.getResourceMethod() : null;
+ if (method == null) {
+ LOG.warn("RequestsInterceptor: resource method is null");
+ return;
+ }
- // Store EntityManager for later retrieval (writer interceptor)
- requestContext.setProperty(EM_CONTEXT_PROPERTY, em);
+ boolean securable = method.isAnnotationPresent(Securable.class);
+ boolean ensureTransaction = method.isAnnotationPresent(EnsureTransaction.class);
- Method method = resourceInfo.getResourceMethod();
+ // Only require injected helpers when the endpoint actually needs them
+ if (securable) {
+ if (tokenHelper == null || cache == null || emProvider == null) {
+ LOG.error(
+ "RequestsInterceptor is not fully initialized for secured endpoint '{}'. " +
+ "tokenHelper={}, cache={}, emProvider={}",
+ method.getName(), tokenHelper, cache, emProvider
+ );
+ requestContext.abortWith(
+ Response.status(Status.INTERNAL_SERVER_ERROR)
+ .entity("Security infrastructure not initialized")
+ .build()
+ );
+ return;
+ }
- if (checkSecurableMethods(requestContext, method)) {
- if (method.isAnnotationPresent(EnsureTransaction.class)) {
- LOG.debug("Beginning transaction");
- em.getTransaction().begin();
+ if (!checkSecurableMethods(requestContext, method)) {
+ return;
+ }
+ }
+
+ // Only open/use EM when needed
+ if (ensureTransaction || securable) {
+ EntityManager em = getEntityManagerSafely();
+ if (em == null) {
+ LOG.error("No EntityManager available for method '{}'", method.getName());
+ requestContext.abortWith(
+ Response.status(Status.INTERNAL_SERVER_ERROR)
+ .entity("Persistence infrastructure not initialized")
+ .build()
+ );
+ return;
+ }
+
+ LOG.debug("GETTING EM: {}", em);
+ requestContext.setProperty(EM_CONTEXT_PROPERTY, em);
+
+ if (ensureTransaction) {
+ try {
+ if (!em.getTransaction().isActive()) {
+ LOG.debug("Beginning transaction");
+ em.getTransaction().begin();
+ }
+ } catch (Exception e) {
+ LOG.error("Error beginning transaction for method '{}'", method.getName(), e);
+ requestContext.abortWith(
+ Response.status(Status.INTERNAL_SERVER_ERROR)
+ .entity("Could not begin transaction")
+ .build()
+ );
+ }
}
}
}
@@ -107,9 +156,11 @@
* @return true if request can proceed; false when aborted
*/
private boolean checkSecurableMethods(ContainerRequestContext ctx, Method method) {
- if (!method.isAnnotationPresent(Securable.class)) return true;
+ if (!method.isAnnotationPresent(Securable.class)) {
+ return true;
+ }
- String token = servletRequest.getHeader(TokenHelper.TOKEN_HEADER_PÀRAM);
+ String token = servletRequest != null ? servletRequest.getHeader(TokenHelper.TOKEN_HEADER_PÀRAM) : null;
if (token == null || !tokenHelper.isTokenValid(token)) {
LOG.warn("Access denied, invalid token");
ctx.abortWith(Response.status(Status.UNAUTHORIZED).build());
@@ -126,11 +177,30 @@
return false;
}
- BasicSecurityContext sc = new BasicSecurityContext(username, roles, servletRequest.isSecure());
+ boolean secure = servletRequest != null && servletRequest.isSecure();
+ BasicSecurityContext sc = new BasicSecurityContext(username, roles, secure);
sc.setOrganizationsIds(getUserOrganizations(username));
sc.setApplicationsIds(getUserApplications(username));
ctx.setSecurityContext(sc);
return true;
+ }
+
+ /**
+ * getEntityManagerSafely<p>
+ * Get the entity manager in a safely way
+ *
+ * @return entityManager
+ */
+ private EntityManager getEntityManagerSafely() {
+ try {
+ if (emProvider == null) {
+ return null;
+ }
+ return emProvider.getEntityManager();
+ } catch (Exception e) {
+ LOG.error("Error obtaining EntityManager from provider", e);
+ return null;
+ }
}
// -------------------------------------------------------------
@@ -145,18 +215,31 @@
* @return userRoles
*/
private int getUserRoles(String username) {
- if (username == null) return 0;
- Integer cached = cache.get("roles_" + username, Integer.class);
- if (cached != null) return cached;
+ if (username == null || cache == null) {
+ return 0;
+ }
- EntityManager em = emProvider.getEntityManager();
+ Integer cached = cache.get("roles_" + username, Integer.class);
+ if (cached != null) {
+ return cached;
+ }
+
+ EntityManager em = getEntityManagerSafely();
+ if (em == null) {
+ LOG.error("Cannot resolve user roles: EntityManager is not available");
+ return 0;
+ }
+
User user = em.find(User.class, username);
int roles = 0;
if (user != null) {
List<Integer> r = user.getRoles();
- if (r != null) for (Integer role : r) roles += role;
+ if (r != null) {
+ for (Integer role : r) {
+ roles += role;
+ }
+ }
cache.set("roles_" + username, roles, 3600);
- // also warm some caches
cache.set("orgs_" + username, user.getOrgsIds(), 3600);
}
return roles;
@@ -171,10 +254,22 @@
* @return userOrganizations
*/
private Set<Integer> getUserOrganizations(String username) {
- Set<Integer> cached = cache.getSet("orgs_" + username, Integer.class);
- if (cached != null) return cached;
+ if (username == null || cache == null) {
+ return Set.of();
+ }
- User user = emProvider.getEntityManager().find(User.class, username);
+ Set<Integer> cached = cache.getSet("orgs_" + username, Integer.class);
+ if (cached != null) {
+ return cached;
+ }
+
+ EntityManager em = getEntityManagerSafely();
+ if (em == null) {
+ LOG.error("Cannot resolve user organizations: EntityManager is not available");
+ return Set.of();
+ }
+
+ User user = em.find(User.class, username);
if (user != null) {
Set<Integer> result = user.getAllOrgsIds();
cache.set("orgs_" + username, result, 3600);
@@ -192,10 +287,22 @@
* @return userApplications
*/
private Set<Integer> getUserApplications(String username) {
- Set<Integer> cached = cache.getSet("apps_" + username, Integer.class);
- if (cached != null) return cached;
+ if (username == null || cache == null) {
+ return Set.of();
+ }
- User user = emProvider.getEntityManager().find(User.class, username);
+ Set<Integer> cached = cache.getSet("apps_" + username, Integer.class);
+ if (cached != null) {
+ return cached;
+ }
+
+ EntityManager em = getEntityManagerSafely();
+ if (em == null) {
+ LOG.error("Cannot resolve user applications: EntityManager is not available");
+ return Set.of();
+ }
+
+ User user = em.find(User.class, username);
if (user != null) {
Set<Integer> result = user.getAllAppsIds();
cache.set("apps_" + username, result, 3600);
@@ -218,30 +325,45 @@
*/
@Override
public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException {
- context.proceed();
-
- EntityManager em = (EntityManager) context.getProperty(EM_CONTEXT_PROPERTY);
- if (em == null) return;
-
try {
- if (em.getTransaction().isActive()) {
- if (servletResponse.getStatus() == Status.OK.getStatusCode()) {
- em.getTransaction().commit();
- LOG.debug("Transaction committed");
- } else {
- em.getTransaction().rollback();
- LOG.debug("Transaction rolled back");
- }
- }
+ context.proceed();
} finally {
- if (em.isOpen()) {
+ EntityManager em = (EntityManager) context.getProperty(EM_CONTEXT_PROPERTY);
+ if (em == null) {
+ return;
+ }
+
+ try {
+ if (em.getTransaction() != null && em.getTransaction().isActive()) {
+ int status = servletResponse != null ? servletResponse.getStatus() : Status.INTERNAL_SERVER_ERROR.getStatusCode();
+ if (status >= 200 && status < 300) {
+ em.getTransaction().commit();
+ LOG.debug("Transaction committed");
+ } else {
+ em.getTransaction().rollback();
+ LOG.debug("Transaction rolled back");
+ }
+ }
+ } catch (Exception e) {
+ LOG.error("Error finalizing transaction", e);
try {
- em.close();
+ if (em.getTransaction() != null && em.getTransaction().isActive()) {
+ em.getTransaction().rollback();
+ }
+ } catch (Exception rollbackEx) {
+ LOG.error("Error rolling back transaction", rollbackEx);
+ }
+ } finally {
+ try {
+ if (em.isOpen()) {
+ em.close();
+ }
} catch (Exception e) {
LOG.error("Error closing EntityManager", e);
}
}
}
}
+
}
diff --git a/securis/src/main/java/net/curisit/securis/services/BasicServices.java b/securis/src/main/java/net/curisit/securis/services/BasicServices.java
index 2c989cc..3b8ce51 100644
--- a/securis/src/main/java/net/curisit/securis/services/BasicServices.java
+++ b/securis/src/main/java/net/curisit/securis/services/BasicServices.java
@@ -28,8 +28,8 @@
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
-import net.curisit.integrity.AppVersion;
-import net.curisit.integrity.commons.Utils;
+import net.curisit.securis.AppVersion;
+import net.curisit.securis.utils.Utils;
import net.curisit.securis.ioc.EnsureTransaction;
import net.curisit.securis.security.Securable;
import net.curisit.securis.utils.TokenHelper;
diff --git a/securis/src/main/java/net/curisit/securis/services/exception/CurisException.java b/securis/src/main/java/net/curisit/securis/services/exception/CurisException.java
new file mode 100644
index 0000000..caaf311
--- /dev/null
+++ b/securis/src/main/java/net/curisit/securis/services/exception/CurisException.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright © 2015 CurisIT, S.L. All Rights Reserved.
+ */
+package net.curisit.securis.services.exception;
+
+/**
+ * CurisException<p>
+ * This class manages the standard (before or after computation) error on computation that implies that the problem is reported
+ * and the computation can continue.
+ *
+ * @author JRA
+ * Last reviewed by APB on April 05, 2022.
+ */
+public class CurisException extends Exception {
+
+ private static final long serialVersionUID = 3830386897219028662L;
+
+ // i18 code for message localization
+ String i18key = null;
+
+ /**
+ * CurisException<p>
+ * This method is used to manage the standard exception with the message.
+ *
+ * @param msg
+ * The exception message
+ */
+ public CurisException(String msg) {
+ super(msg);
+ }
+
+ /**
+ * CurisException<p>
+ * This method is used to manage the standard exception with the message and the cause.
+ *
+ * @param msg
+ * The exception message
+ * @param cause
+ * The error cause
+ */
+ public CurisException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+
+ /**
+ * CurisException<p>
+ * This method is used to manage the standard exception with the message and the cause.
+ *
+ * @param msg
+ * The exception message
+ * @param cause
+ * The error cause
+ */
+ public CurisException(String msg, String i18k) {
+ this(msg);
+ this.i18key = i18k;
+ }
+
+ /**
+ * CurisException<p>
+ * This method is used to manage the standard exception with the message, the i18 code and the cause.
+ *
+ * @param msg
+ * The exception message
+ * @param i18k
+ * The code for localization
+ * @param cause
+ * The error cause
+ */
+ public CurisException(String msg, String i18k, Throwable cause) {
+ this(msg, cause);
+ this.i18key = i18k;
+ }
+
+ /**
+ * geti18key<p>
+ * This method is used to get the i18 code for localization.
+ *
+ * @return i18key
+ * The i18 code
+ */
+ public String geti18key() {
+ return i18key;
+ }
+
+}
diff --git a/securis/src/main/java/net/curisit/securis/services/exception/SeCurisServiceException.java b/securis/src/main/java/net/curisit/securis/services/exception/SeCurisServiceException.java
index e44e944..5dde215 100644
--- a/securis/src/main/java/net/curisit/securis/services/exception/SeCurisServiceException.java
+++ b/securis/src/main/java/net/curisit/securis/services/exception/SeCurisServiceException.java
@@ -3,7 +3,7 @@
*/
package net.curisit.securis.services.exception;
-import net.curisit.integrity.exception.CurisException;
+
/**
* SeCurisServiceException
diff --git a/securis/src/main/java/net/curisit/securis/utils/Utils.java b/securis/src/main/java/net/curisit/securis/utils/Utils.java
new file mode 100644
index 0000000..b6fac36
--- /dev/null
+++ b/securis/src/main/java/net/curisit/securis/utils/Utils.java
@@ -0,0 +1,719 @@
+/*
+ * Copyright @ 2013 CurisTEC, S.A.S. All Rights Reserved.
+ */
+package net.curisit.securis.utils;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.lang.reflect.InvocationTargetException;
+import java.math.BigInteger;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+import java.nio.charset.Charset;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.StandardOpenOption;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.TimeZone;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+import java.util.concurrent.TimeoutException;
+import java.util.function.Supplier;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+import org.apache.commons.beanutils.BeanUtils;
+import org.apache.commons.beanutils.PropertyUtils;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import net.curisit.integrity.exception.CurisRuntimeException;
+
+/**
+ * Utils
+ * <p>
+ * General-purpose static utilities: hashing, dates/times, collections, I/O helpers,
+ * simple email validation, reflection helpers, file tailing, etc.
+ *
+ * Author: JRA
+ * Last reviewed by JRA on Oct 6, 2025.
+ */
+public class Utils {
+
+ private static final Logger LOG = LogManager.getLogger(Utils.class);
+
+ /* --------------------- Hash functions --------------------- */
+
+ /**
+ * md5<p>
+ * Returns MD5 digest (hex, 32 chars) for the input text.
+ *
+ * @param str source text
+ * @return lowercase hex MD5 or null on error
+ */
+ public static String md5(String str) {
+ try {
+ MessageDigest mDigest = MessageDigest.getInstance("MD5");
+ mDigest.update(str.getBytes(), 0, str.length());
+ BigInteger i = new BigInteger(1, mDigest.digest());
+ return String.format("%1$032x", i);
+ } catch (NoSuchAlgorithmException e) {
+ LOG.error("Error generating MD5 for string: " + str, e);
+ }
+ return null;
+ }
+
+ /**
+ * sha1<p>
+ * Returns SHA-1 digest (hex, 40 chars) for the input text.
+ *
+ * @param str source text
+ * @return lowercase hex SHA-1 or null on error
+ */
+ public static String sha1(String str) {
+ try {
+ MessageDigest mDigest = MessageDigest.getInstance("SHA1");
+ mDigest.update(str.getBytes(), 0, str.length());
+ BigInteger i = new BigInteger(1, mDigest.digest());
+ return String.format("%1$040x", i);
+ } catch (NoSuchAlgorithmException e) {
+ LOG.error("Error generating SHA1 for string: " + str, e);
+ }
+ return null;
+ }
+
+ /**
+ * sha256<p>
+ * Returns SHA-256 digest (hex, 64 chars) for the input text.
+ *
+ * @param str source text
+ * @return lowercase hex SHA-256 or null on error
+ */
+ public static String sha256(String str) {
+ return sha256(str.getBytes());
+ }
+
+ /**
+ * sha256<p>
+ * Returns SHA-256 digest (hex) for a byte array.
+ *
+ * @param bytes data
+ * @return hex SHA-256 or null on error
+ */
+ public static String sha256(byte[] bytes) {
+ try {
+ MessageDigest mDigest = MessageDigest.getInstance("SHA-256");
+ mDigest.update(bytes, 0, bytes.length);
+ BigInteger i = new BigInteger(1, mDigest.digest());
+ return String.format("%1$064x", i);
+ } catch (NoSuchAlgorithmException e) {
+ LOG.error("Error generating SHA-256 for bytes: " + bytes, e);
+ }
+ return null;
+ }
+
+ /**
+ * sha256<p>
+ * Incrementally updates SHA-256 with multiple byte arrays and returns hex digest.
+ *
+ * @param bytes multiple byte chunks
+ * @return hex SHA-256 or null on error
+ */
+ public static String sha256(byte[]... bytes) {
+ try {
+ MessageDigest mDigest = MessageDigest.getInstance("SHA-256");
+ for (byte[] bs : bytes) {
+ mDigest.update(bs, 0, bs.length);
+ }
+ BigInteger i = new BigInteger(1, mDigest.digest());
+ return String.format("%1$064x", i);
+ } catch (NoSuchAlgorithmException e) {
+ LOG.error("Error generating SHA-256 for bytes: " + bytes, e);
+ }
+ return null;
+ }
+
+ /* --------------------- ISO date helpers --------------------- */
+
+ private static final String ISO_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSSZ";
+ private static final SimpleDateFormat sdf = new SimpleDateFormat(ISO_PATTERN);
+ static {
+ sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
+ }
+
+ /**
+ * toIsoFormat<p>
+ * Formats a Date to ISO-8601-like string with milliseconds in GMT.
+ *
+ * @param date input date
+ * @return formatted string
+ */
+ public static synchronized String toIsoFormat(Date date) {
+ return sdf.format(date);
+ }
+
+ private static final String ISO_PATTERN_REDUCED_PRECISION = "yyyy-MM-dd'T'HH:mm:ssZ";
+ private static final SimpleDateFormat sdfReduced = new SimpleDateFormat(ISO_PATTERN_REDUCED_PRECISION);
+ static {
+ sdfReduced.setTimeZone(TimeZone.getTimeZone("GMT"));
+ }
+
+ /**
+ * toIsoFormatReduced<p>
+ * Formats a Date to ISO string without milliseconds in GMT.
+ *
+ * @param date input date
+ * @return formatted string
+ */
+ public static synchronized String toIsoFormatReduced(Date date) {
+ return sdfReduced.format(date);
+ }
+
+ /**
+ * toDateFromIso<p>
+ * Parses a string in {@link #ISO_PATTERN} into a Date (GMT).
+ *
+ * @param dateStr string to parse
+ * @return Date or null if parsing fails
+ */
+ public static synchronized Date toDateFromIso(String dateStr) {
+ try {
+ return sdf.parse(dateStr);
+ } catch (ParseException e) {
+ LOG.error("Error parsing string '{}' to Date object with pattern: {}", dateStr, ISO_PATTERN);
+ return null;
+ }
+ }
+
+ /* --------------------- Null-safe helpers --------------------- */
+
+ /**
+ * nonull<p>
+ * Returns {@code value} cast to T, or {@code defaultValue} if null.
+ *
+ * @param value input
+ * @param defaultValue fallback
+ * @return value or default
+ */
+ @SuppressWarnings("unchecked")
+ public static <T> T nonull(Object value, T defaultValue) {
+ return value == null ? defaultValue : (T) value;
+ }
+
+ /**
+ * nonull<p>
+ * Returns String value or empty string if null.
+ *
+ * @param value input
+ * @return non-null string
+ */
+ public static String nonull(Object value) {
+ return nonull(value, "");
+ }
+
+ /**
+ * value<p>
+ * Unchecked cast helper (avoid using unless necessary).
+ *
+ * @param value object
+ * @param type target type
+ * @return casted value
+ */
+ @SuppressWarnings("unchecked")
+ public static <T> T value(Object value, Class<T> type) {
+ return (T) value;
+ }
+
+ /**
+ * createMap<p>
+ * Convenience to create a Map from varargs key/value pairs.
+ *
+ * @param keyValue alternating key, value
+ * @return map with pairs
+ */
+ @SuppressWarnings("unchecked")
+ public static <K extends Object, V extends Object> Map<K, V> createMap(Object... keyValue) {
+ Map<K, V> map = (Map<K, V>) new HashMap<Object, Object>();
+ for (int i = 0; i < keyValue.length; i += 2) {
+ ((Map<Object, Object>) map).put(keyValue[i], keyValue[i + 1]);
+ }
+ return map;
+ }
+
+ /* --------------------- Date arithmetic --------------------- */
+
+ /**
+ * addDays<p>
+ * Adds a number of days to a Date.
+ *
+ * @param date base date
+ * @param days positive/negative days
+ * @return new Date
+ */
+ public static Date addDays(Date date, int days) {
+ Calendar cal = Calendar.getInstance();
+ cal.setTime(date);
+ cal.add(Calendar.DATE, days);
+ return cal.getTime();
+ }
+
+ /**
+ * addHours<p>
+ * Adds hours to a Date.
+ *
+ * @param date base date
+ * @param hours number of hours
+ * @return new Date
+ */
+ public static Date addHours(Date date, int hours) {
+ Calendar cal = Calendar.getInstance();
+ cal.setTime(date);
+ cal.add(Calendar.HOUR, hours);
+ return cal.getTime();
+ }
+
+ /**
+ * addMinutes<p>
+ * Adds minutes to a Date.
+ *
+ * @param date base date
+ * @param minutes number of minutes
+ * @return new Date
+ */
+ public static Date addMinutes(Date date, int minutes) {
+ Calendar cal = Calendar.getInstance();
+ cal.setTime(date);
+ cal.add(Calendar.MINUTE, minutes);
+ return cal.getTime();
+ }
+
+ /**
+ * getStrTime<p>
+ * Formats seconds as H:MM:SS (H omitted if 0). Negative yields "??".
+ *
+ * @param seconds total seconds
+ * @return formatted time
+ */
+ public static String getStrTime(int seconds) {
+ if (seconds < 0)
+ return "??";
+ String secs = String.format("%02d", seconds % 60);
+ String hours = "";
+ String mins = "0:";
+ if (seconds > 3600) {
+ hours = String.format("%d:", seconds / 3600);
+ }
+ if (seconds > 60) {
+ mins = String.format("%02d:", (seconds / 60) % 60);
+ }
+ return hours + mins + secs;
+ }
+
+ /* --------------------- Collections & conversions --------------------- */
+
+ /**
+ * areAllEmpty<p>
+ * Checks if all provided collections are empty.
+ *
+ * @param lists vararg of collections
+ * @return true if all empty
+ */
+ public static boolean areAllEmpty(Collection<?>... lists) {
+ for (Collection<?> list : lists) {
+ if (!list.isEmpty())
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * toDateFromLocalDate<p>
+ * Converts a {@link LocalDate} to {@link Date} at system default zone start of day.
+ *
+ * @param ld local date
+ * @return Date or null
+ */
+ public static Date toDateFromLocalDate(LocalDate ld) {
+ if (ld == null)
+ return null;
+ Instant instant = ld.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant();
+ return Date.from(instant);
+ }
+
+ /**
+ * toLocalDateFromDate<p>
+ * Converts a {@link Date} to {@link LocalDate} at system default zone.
+ *
+ * @param date Date
+ * @return LocalDate or null
+ */
+ public static LocalDate toLocalDateFromDate(Date date) {
+ if (date == null)
+ return null;
+ Instant instant = Instant.ofEpochMilli(date.getTime());
+ return LocalDateTime.ofInstant(instant, ZoneId.systemDefault()).toLocalDate();
+ }
+
+ /**
+ * compare<p>
+ * Null-safe equals: both null → true; otherwise {@code equals()}.
+ *
+ * @param obj1 first object
+ * @param obj2 second object
+ * @return equality result
+ */
+ public static boolean compare(Object obj1, Object obj2) {
+ if (obj1 == null) {
+ return obj2 == null;
+ } else {
+ return obj1.equals(obj2);
+ }
+ }
+
+ /* --------------------- Email validation --------------------- */
+
+ private static final String EMAIL_PATTERN_STR = "^[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*@" + "[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$";
+ private static final Pattern EMAIL_PATTERN = Pattern.compile(EMAIL_PATTERN_STR);
+
+ /**
+ * isValidEmail<p>
+ * Validates an email address format with a basic regex.
+ *
+ * @param email candidate value
+ * @return valid or not
+ */
+ public static boolean isValidEmail(final String email) {
+ if (email == null) {
+ return false;
+ }
+ return EMAIL_PATTERN.matcher(email).matches();
+ }
+
+ /* --------------------- Iterable helpers --------------------- */
+
+ /**
+ * Reversed<p>
+ * Iterable wrapper to iterate a List in reverse order without copying.
+ *
+ * @param <T> element type
+ */
+ public static class Reversed<T> implements Iterable<T> {
+ private final List<T> original;
+
+ public Reversed(List<T> original) {
+ this.original = original;
+ }
+
+ public Iterator<T> iterator() {
+ return new Iterator<T>() {
+ int cursor = original.size();
+ public boolean hasNext() { return cursor > 0; }
+ public T next() { return original.get(--cursor); }
+ public void remove() { throw new CurisRuntimeException("ERROR removing item in read-only iterator"); }
+ };
+ }
+ }
+
+ /**
+ * reversedList<p>
+ * Returns a reversed-iteration view of a list.
+ *
+ * @param original list to view
+ * @param <T> element type
+ * @return iterable reverse view
+ */
+ public static <T> Reversed<T> reversedList(List<T> original) {
+ return new Reversed<T>(original);
+ }
+
+ /**
+ * isSorted<p>
+ * Checks if a list of Doubles is non-decreasing.
+ *
+ * @param original list
+ * @return true if sorted ascending
+ */
+ public static boolean isSorted(List<Double> original) {
+ for (int i = 0; i < (original.size() - 1); i++) {
+ Double v0 = original.get(i);
+ Double v1 = original.get(i + 1);
+ if (v0.doubleValue() > v1.doubleValue()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /* --------------------- Simple file reading helpers --------------------- */
+
+ /**
+ * readFileLines<p>
+ * Reads a file as UTF-8 and splits each line by a separator.
+ *
+ * @param file source file
+ * @param separator regex separator
+ * @return list of line fields
+ * @throws IOException on I/O errors
+ */
+ public static List<List<String>> readFileLines(File file, final String separator) throws IOException {
+ List<String> lines = Files.readAllLines(Paths.get(file.toURI()), Charset.forName("utf8"));
+ return lines.stream().map(line -> Arrays.asList(line.split(separator))).collect(Collectors.toList());
+ }
+
+ /**
+ * readStreamLines<p>
+ * Reads an InputStream as UTF-8 and splits each line by a separator.
+ *
+ * @param stream input stream (caller closes it)
+ * @param separator regex separator
+ * @return list of line fields
+ * @throws IOException on I/O errors
+ */
+ public static List<List<String>> readStreamLines(InputStream stream, final String separator) throws IOException {
+ try (
+ InputStreamReader isr = new InputStreamReader(stream, Charset.forName("UTF-8"));
+ BufferedReader br = new BufferedReader(isr);
+ ) {
+ String line;
+ List<List<String>> lines = new ArrayList<>();
+ while ((line = br.readLine()) != null) {
+ lines.add(Arrays.asList(line.split(separator)));
+ }
+ return lines;
+ }
+ }
+
+ /**
+ * deleteDirectoryTree<p>
+ * Recursively deletes a directory and its contents.
+ *
+ * @param baseDirectory directory path
+ * @throws IOException on failure
+ */
+ public static void deleteDirectoryTree(Path baseDirectory) throws IOException {
+ Files.walkFileTree(baseDirectory, new SimpleFileVisitor<Path>() {
+ @Override
+ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
+ Files.deleteIfExists(file);
+ return FileVisitResult.CONTINUE;
+ }
+ @Override
+ public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
+ Files.deleteIfExists(dir);
+ return FileVisitResult.CONTINUE;
+ }
+ });
+ }
+
+ /* --------------------- Directory size & tail --------------------- */
+
+ private static class LongWrapper { long num = 0L; }
+
+ /**
+ * getDirectoryContentSize<p>
+ * Computes total size (bytes) of files within a directory tree.
+ *
+ * @param baseDirectory root path
+ * @return total bytes
+ * @throws IOException on traversal errors
+ */
+ public static long getDirectoryContentSize(Path baseDirectory) throws IOException {
+ final LongWrapper size = new LongWrapper();
+ Files.walkFileTree(baseDirectory, new SimpleFileVisitor<Path>() {
+ @Override
+ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
+ size.num += file.toFile().length();
+ return FileVisitResult.CONTINUE;
+ }
+ @Override
+ public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
+ if (!dir.equals(baseDirectory)) {
+ size.num += dir.toFile().length();
+ }
+ return FileVisitResult.CONTINUE;
+ }
+ });
+ return size.num;
+ }
+
+ /**
+ * waitFor<p>
+ * Asynchronously waits until a condition is true or a timeout is reached.
+ *
+ * @param condition supplier that should eventually return true
+ * @param timeout max wait (ms)
+ * @return future completing with true if condition met
+ * @throws TimeoutException surfaced through CompletionException if timed out
+ */
+ public static CompletableFuture<Boolean> waitFor(Supplier<Boolean> condition, long timeout) throws TimeoutException {
+ return CompletableFuture.<Boolean>supplyAsync(() -> {
+ long t0 = System.currentTimeMillis();
+ while(!condition.get()) {
+ long t1 = System.currentTimeMillis();
+ if ((t1 - t0) > timeout) {
+ throw new CompletionException(new TimeoutException());
+ }
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ throw new CompletionException(e);
+ }
+ }
+ return true;
+ });
+ }
+
+ private static final int BLOCK_SIZE = 4096; // 4KB
+
+ /**
+ * getLastLines<p>
+ * Efficiently reads the last N lines of a text file by scanning from the end in blocks.
+ *
+ * @param file file to read
+ * @param numLines number of lines from the end
+ * @return list of lines in natural order (oldest→newest among the last N)
+ * @throws IOException on I/O errors
+ */
+ public static List<String> getLastLines(File file, int numLines) throws IOException {
+ LinkedList<String> lines = new LinkedList<>();
+ long fileSize = file.length();
+ int chunk = (fileSize < BLOCK_SIZE) ? (int)fileSize : BLOCK_SIZE;
+ long seekPos = fileSize - chunk;
+ FileChannel fc = FileChannel.open(file.toPath(), StandardOpenOption.READ);
+ final byte newLine = (byte)'\n';
+ String partialLine = null;
+ while (lines.size() < numLines) {
+ ByteBuffer dst = ByteBuffer.allocate(chunk);
+ int bytesRead = fc.read(dst, seekPos);
+ byte[] content = dst.array();
+ int lastLineIdx = bytesRead;
+ for (int i = bytesRead - 1; i > 0 ; i--) {
+ byte b = content[i];
+ if (b == newLine) {
+ if (i < (bytesRead - 1) || partialLine != null) {
+ String line = new String(Arrays.copyOfRange(content, i, lastLineIdx)).trim();
+ if (partialLine != null) {
+ line += partialLine;
+ partialLine = null;
+ }
+ lines.addFirst(line);
+ if (lines.size() == numLines) {
+ return lines;
+ }
+ }
+ lastLineIdx = i;
+ }
+ }
+ if (lastLineIdx > 0) {
+ if (partialLine == null) {
+ partialLine = new String(Arrays.copyOfRange(content, 0, lastLineIdx)).trim();
+ } else {
+ partialLine = new String(Arrays.copyOfRange(content, 0, lastLineIdx)) + partialLine;
+ }
+ }
+ if (seekPos == 0) {
+ lines.addFirst(partialLine);
+ break;
+ }
+ if (seekPos < BLOCK_SIZE) {
+ chunk = (int)seekPos;
+ seekPos = 0;
+ } else {
+ seekPos -= BLOCK_SIZE;
+ }
+ }
+ return lines;
+ }
+
+ /**
+ * removeDuplicates<p>
+ * Returns a sorted list without duplicates using a set roundtrip.
+ *
+ * @param list input list
+ * @param <T> type must be Comparable
+ * @return sorted distinct list
+ */
+ public static <T> List<T> removeDuplicates(List<T> list) {
+ return new HashSet<>(list).stream().sorted().collect(Collectors.toList());
+ }
+
+ /* --------------------- Reflection helpers --------------------- */
+
+ /**
+ * setProperty<p>
+ * Sets a bean property by name using Apache BeanUtils PropertyUtils.
+ *
+ * @param bean target bean
+ * @param fieldName property name (nested supported)
+ * @param value value to assign
+ * @throws RuntimeException wrapping reflection errors
+ */
+ public static void setProperty(Object bean, String fieldName, Object value) {
+ try {
+ PropertyUtils.setProperty(bean, fieldName, value);
+ } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
+ LOG.error("Missing field:" + fieldName);
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * getProperty<p>
+ * Reads a bean property by name using Apache PropertyUtils.
+ *
+ * @param bean target bean
+ * @param fieldName property name
+ * @return value
+ * @throws RuntimeException wrapping reflection errors
+ */
+ public static Object getProperty(Object bean, String fieldName) {
+ try {
+ return PropertyUtils.getProperty(bean, fieldName);
+ } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
+ LOG.error("Missing field:" + fieldName);
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * cloneBean<p>
+ * Clones a bean using Apache BeanUtils' copy mechanisms (shallow copy).
+ *
+ * @param bean source bean
+ * @return new cloned bean
+ * @throws CurisRuntimeException on cloning errors
+ */
+ public static Object cloneBean(Object bean) {
+ try {
+ return BeanUtils.cloneBean(bean);
+ } catch (IllegalAccessException | InstantiationException | InvocationTargetException | NoSuchMethodException e) {
+ throw new CurisRuntimeException("Error cloning bean", e);
+ }
+ }
+}
+
diff --git a/securis/src/main/webapp/WEB-INF/web.xml b/securis/src/main/webapp/WEB-INF/web.xml
index a833683..1b5d120 100644
--- a/securis/src/main/webapp/WEB-INF/web.xml
+++ b/securis/src/main/webapp/WEB-INF/web.xml
@@ -36,11 +36,13 @@
<param-name>resteasy.providers</param-name>
<param-value>net.curisit.securis.DefaultExceptionHandler</param-value>
</context-param>
+ <!--
<context-param>
<param-name>resteasy.injector.factory</param-name>
<param-value>org.jboss.resteasy.cdi.CdiInjectorFactory</param-value>
</context-param>
-
+ -->
+
<filter>
<filter-name>DevFilter</filter-name>
<filter-class>
@@ -49,7 +51,7 @@
</filter>
<filter-mapping>
<filter-name>DevFilter</filter-name>
- <url-pattern>/*</url-pattern>
+ <url-pattern>/api/*</url-pattern>
</filter-mapping>
<filter>
@@ -63,49 +65,50 @@
<url-pattern>/main-bundle.js</url-pattern>
</filter-mapping>
- <filter>
- <filter-name>Resteasy</filter-name>
- <filter-class>
- org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher
- </filter-class>
- <init-param>
- <param-name>jakarta.ws.rs.Application</param-name>
- <param-value>net.curisit.securis.RestServicesApplication</param-value>
- </init-param>
- </filter>
-
- <filter-mapping>
- <filter-name>Resteasy</filter-name>
- <url-pattern>/*</url-pattern>
- </filter-mapping>
+ <servlet>
+ <servlet-name>Resteasy</servlet-name>
+ <servlet-class>org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher</servlet-class>
+ <init-param>
+ <param-name>jakarta.ws.rs.Application</param-name>
+ <param-value>net.curisit.securis.RestServicesApplication</param-value>
+ </init-param>
+ <load-on-startup>1</load-on-startup>
+ </servlet>
+
+ <servlet-mapping>
+ <servlet-name>Resteasy</servlet-name>
+ <url-pattern>/api/*</url-pattern>
+ </servlet-mapping>
<welcome-file-list>
- <welcome-file>/index.jsp</welcome-file>
+ <welcome-file>index.jsp</welcome-file>
</welcome-file-list>
- <!-- Security roles referenced by this web application -->
- <security-role>
- <description>
- Advance users, customers
- </description>
- <role-name>advance</role-name>
- </security-role>
- <security-role>
- <description>
- Administrator role
- </description>
- <role-name>admin</role-name>
- </security-role>
+ <!-- Security roles referenced by this web application -->
+ <security-role>
+ <description>
+ Advance users, customers
+ </description>
+ <role-name>advance</role-name>
+ </security-role>
+ <security-role>
+ <description>
+ Administrator role
+ </description>
+ <role-name>admin</role-name>
+ </security-role>
-<resource-env-ref>
- <resource-env-ref-name>SeCurisDS</resource-env-ref-name>
- <resource-env-ref-type>jakarta.sql.DataSource</resource-env-ref-type>
-</resource-env-ref>
-
-<resource-env-ref>
- <resource-env-ref-name>BeanManager</resource-env-ref-name>
- <resource-env-ref-type>jakarta.enterprise.inject.spi.BeanManager</resource-env-ref-type>
-</resource-env-ref>
+ <resource-env-ref>
+ <resource-env-ref-name>SeCurisDS</resource-env-ref-name>
+ <resource-env-ref-type>jakarta.sql.DataSource</resource-env-ref-type>
+ </resource-env-ref>
+
+ <!--
+ <resource-env-ref>
+ <resource-env-ref-name>BeanManager</resource-env-ref-name>
+ <resource-env-ref-type>jakarta.enterprise.inject.spi.BeanManager</resource-env-ref-type>
+ </resource-env-ref>
+ -->
</web-app>
\ No newline at end of file
--
Gitblit v1.3.2