Compare commits

...

No commits in common. "main" and "pranical/feature/jsonhelper-update" have entirely different histories.

42 changed files with 2126 additions and 2 deletions

38
.gitignore vendored Normal file
View File

@ -0,0 +1,38 @@
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
### IntelliJ IDEA ###
.idea/modules.xml
.idea/jarRepositories.xml
.idea/compiler.xml
.idea/libraries/
*.iws
*.iml
*.ipr
### Eclipse ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/
### VS Code ###
.vscode/
### Mac OS ###
.DS_Store

3
.idea/.gitignore generated vendored Normal file
View File

@ -0,0 +1,3 @@
# Default ignored files
/shelf/
/workspace.xml

7
.idea/encodings.xml generated Normal file
View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding">
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
</component>
</project>

14
.idea/misc.xml generated Normal file
View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="MavenProjectsManager">
<option name="originalFiles">
<list>
<option value="$PROJECT_DIR$/pom.xml" />
</list>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="temurin-17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

6
.idea/vcs.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

10
CHANGELOG.md Normal file
View File

@ -0,0 +1,10 @@
# Changelog
Todos los cambios notables en este proyecto se documentarán en este archivo.
El formato se basa en [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
y este proyecto se adhiere a [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [UNRELEASE]
- Base inicial commons nativo (Java)

21
LICENCE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024 Banesco Banco Universal, C.A.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1,3 +1,10 @@
# commons-plain-java
commons en Java puro para usar con APIs Quarkus y/o BIAN
![Logo](https://upload.wikimedia.org/wikipedia/commons/thumb/7/74/Banesco_logo.svg/2560px-Banesco_logo.svg.png)
# Commons
Este proyecto utiliza Java nativo como librería commons
- Banesco Banco Universal &copy; Todos los Derechos Reservados.

85
pom.xml Normal file
View File

@ -0,0 +1,85 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.banesco</groupId>
<artifactId>commons</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
<name>commons</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<java.version>17</java.version>
<jakarta.jaxb.api.version>4.0.0</jakarta.jaxb.api.version>
<jakarta.jaxb.impl.version>4.0.5</jakarta.jaxb.impl.version>
</properties>
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.4</version>
<scope>compile</scope>
</dependency>
<!--dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.13.0</version>
</dependency-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<!-- Dependencias de JAXB para JDK 17 (Jakarta EE) -->
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
<version>${jakarta.jaxb.api.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>${jakarta.jaxb.impl.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.17.1</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>17</source>
<target>17</target>
<release>17</release>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.3.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>3.3.1</version>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,8 @@
package com.banesco;
public class Commons {
public static void main(String[] args) {
System.out.println("Commons...");
}
}

View File

@ -0,0 +1,33 @@
package com.banesco.common.application.exception;
public class BanBackendException extends RuntimeException {
protected String backendCode;
protected String backendDesc;
protected String target;
public BanBackendException(String backendCode, String backendDesc) {
super(backendDesc);
this.backendCode = backendCode;
this.backendDesc = backendDesc;
}
public BanBackendException(String backendCode, String backendDesc, String target) {
super(backendDesc.formatted(target) + ", Target:" + target);
this.backendCode = backendCode;
this.backendDesc = backendDesc;
this.target = target;
}
public String getBackendCode() {
return backendCode;
}
public String getBackendDesc() {
return backendDesc;
}
public String getTarget() {
return target;
}
}

View File

@ -0,0 +1,32 @@
package com.banesco.common.application.exception;
import com.banesco.common.domain.interfaces.BaseStatusCodesEnum;
public class BanRuntimeException extends RuntimeException {
protected BaseStatusCodesEnum statusCode;
protected String target;
public BanRuntimeException() {
super("Internal Error");
}
public BanRuntimeException(BaseStatusCodesEnum statusCode, String target) {
super(statusCode.getStatusDesc().formatted(target) + ", Target:" + target);
this.statusCode = statusCode;
this.target = target;
}
public BanRuntimeException(BaseStatusCodesEnum statusCode) {
super(statusCode.getStatusDesc());
this.statusCode = statusCode;
}
public BaseStatusCodesEnum getStatusCode() {
return statusCode;
}
public String getTarget() {
return target;
}
}

View File

@ -0,0 +1,36 @@
package com.banesco.common.domain.dto.bian.customer;
public class CustomerReference {
String customerIdType;
String customerId;
public CustomerReference() {
}
public CustomerReference(String customerIdType, String customerId) {
this.customerIdType = customerIdType;
this.customerId = customerId;
}
public String getCustomerIdType() {
return customerIdType;
}
public void setCustomerIdType(String customerIdType) {
this.customerIdType = customerIdType;
}
public String getCustomerId() {
return customerId;
}
public void setCustomerId(String customerId) {
this.customerId = customerId;
}
@Override
public String toString() {
return "{\"customerIdType\":\"%s\",\"customerId\":\"%s\"}"
.formatted(customerIdType, customerId);
}
}

View File

@ -0,0 +1,59 @@
package com.banesco.common.domain.dto.bian.device;
public class BianDevice {
String deviceType;
String deviceDescription;
String deviceIp;
String deviceSessionReference;
public BianDevice() {
}
public BianDevice(String deviceType, String deviceDescription, String deviceIp, String deviceSessionReference) {
this.deviceType = deviceType;
this.deviceDescription = deviceDescription;
this.deviceIp = deviceIp;
this.deviceSessionReference = deviceSessionReference;
}
public String getDeviceType() {
return deviceType;
}
public void setDeviceType(String deviceType) {
this.deviceType = deviceType;
}
public String getDeviceDescription() {
return deviceDescription;
}
public void setDeviceDescription(String deviceDescription) {
this.deviceDescription = deviceDescription;
}
public String getDeviceIp() {
return deviceIp;
}
public void setDeviceIp(String deviceIp) {
this.deviceIp = deviceIp;
}
public String getDeviceSessionReference() {
return deviceSessionReference;
}
public void setDeviceSessionReference(String deviceSessionReference) {
this.deviceSessionReference = deviceSessionReference;
}
public String toString() {
return "{\"deviceType\":\"%s\",\"deviceDescription\":\"%s\",\"deviceIp\":\"%s\",\"deviceSessionReference\":\"%s\"}"
.formatted(deviceType, deviceDescription, deviceIp, deviceSessionReference);
}
}

View File

@ -0,0 +1,6 @@
package com.banesco.common.domain.dto.bian.paymentCard;
public enum CardTypeValues {
DebitCard,
CreditCard
}

View File

@ -0,0 +1,33 @@
package com.banesco.common.domain.dto.bian.response;
public class BaseResponse<T, K> {
protected T statusResponse;
protected K dataResponse;
public BaseResponse() {
}
public BaseResponse(T statusResponse, K dataResponse) {
this.statusResponse = statusResponse;
this.dataResponse = dataResponse;
}
public T getStatusResponse() {
return statusResponse;
}
public void setStatusResponse(T statusResponse) {
this.statusResponse = statusResponse;
}
public K getDataResponse() {
return dataResponse;
}
public void setDataResponse(K statusResponse) {
this.dataResponse = dataResponse;
}
}

View File

@ -0,0 +1,10 @@
package com.banesco.common.domain.dto.bian.response;
public record BianStatusResponse(String status, String statusCode, String message, String traceId) {
@Override
public String toString() {
return "{\"status\":\"%s\",\"statusCode\":\"%s\",\"message\":\"%s\",\"traceId\":\"%s\"}".formatted(status, statusCode, message, traceId);
}
}

View File

@ -0,0 +1,10 @@
package com.banesco.common.domain.dto.bian.response;
public record MetadataResponse(String timestamp, String operationId) {
@Override
public String toString() {
return "{\"timestamp\":\"%s\",\"operationId\":\"%s\"}".formatted(timestamp, operationId);
}
}

View File

@ -0,0 +1,6 @@
package com.banesco.common.domain.interfaces;
public interface BaseStatusCodesEnum {
String getStatusCode();
String getStatusDesc();
}

View File

@ -0,0 +1,54 @@
package com.banesco.common.domain.model;
import java.util.Map;
/**
* Clase de configuración para APIs. Almacena la información necesaria para
* conectarse a un servicio API externo, incluyendo URL, configuración de
* timeout y parámetros adicionales. Diseñada para ser utilizada como objeto de
* configuración en clientes de API y servicios de integración.
*/
public class ApiConfig {
String url;
ApiTimeout timeout;
Map<String, String> config;
public ApiConfig(){
}
public ApiConfig(String url,
ApiTimeout timeout,
Map<String, String> config) {
this.url = url;
this.timeout = timeout;
this.config = config;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public ApiTimeout getTimeout() {
return timeout;
}
public void setTimeout(ApiTimeout timeout) {
this.timeout = timeout;
}
public Map<String, String> getConfig() {
return config;
}
public void setConfig(Map<String, String> config) {
this.config = config;
}
}

View File

@ -0,0 +1,37 @@
package com.banesco.common.domain.model;
/**
* Clase interna que representa la configuración de tiempos de espera
* (timeout) para conexiones a APIs. Define los tiempos máximos de espera
* para establecer la conexión y para recibir una respuesta, especificados
* en milisegundos.
*/
public class ApiTimeout{
private int connect;
private int response;
public ApiTimeout() {
}
public ApiTimeout(int connect, int response) {
this.connect = connect;
this.response = response;
}
public int getConnect() {
return connect;
}
public void setConnect(int connect) {
this.connect = connect;
}
public int getResponse() {
return response;
}
public void setResponse(int response) {
this.response = response;
}
}

View File

@ -0,0 +1,63 @@
package com.banesco.common.domain.model;
/**
* Clase para obtener los codigos y mensajes de salida
*/
public class BackResponse {
private String backendCode;
private int httpCode;
private String statusCode;
private String description;
public BackResponse() {
}
public BackResponse(String backendCode,
int httpCode,
String statusCode,
String description) {
this.backendCode = backendCode;
this.httpCode = httpCode;
this.statusCode = statusCode;
this.description = backendCode;
}
public String getBackendCode() {
return backendCode;
}
public int getHttpCode() {
return httpCode;
}
public String getStatusCode() {
return statusCode;
}
public String getDescription() {
return description;
}
public void setBackendCode(String backendCode) {
this.backendCode = backendCode;
}
public void setHttpCode(int httpCode) {
this.httpCode = httpCode;
}
public void setStatusCode(String statusCode) {
this.statusCode = statusCode;
}
public void setDescription(String description) {
this.description = description;
}
@Override
public String toString() {
return "{\"backendCode\":\"%s\",\"httpCode\":%d,\"statusCode\":\"%s\",\"description\":\"%s\"}"
.formatted(backendCode, httpCode, statusCode, description);
}
}

View File

@ -0,0 +1,36 @@
package com.banesco.common.domain.model;
public class BaseResponse<T> {
protected StatusResponse statusResponse;
protected T dataResponse;
public BaseResponse() {
}
public BaseResponse(StatusResponse statusResponse) {
this.statusResponse = statusResponse;
}
public BaseResponse(StatusResponse statusResponse, T dataResponse) {
this.statusResponse = statusResponse;
this.dataResponse = dataResponse;
}
public StatusResponse getStatusResponse() {
return statusResponse;
}
public void setStatusResponse(StatusResponse statusResponse) {
this.statusResponse = statusResponse;
}
public T getDataResponse() {
return dataResponse;
}
public void setDataResponse(T dataResponse) {
this.dataResponse = dataResponse;
}
}

View File

@ -0,0 +1,50 @@
package com.banesco.common.domain.model;
/**
*
*/
public class Device {
private String type;
private String description;
private String ipAddress;
public Device() {
}
public Device(String type, String description, String ipAddress) {
this.type = type;
this.description = description;
this.ipAddress = ipAddress;
}
public String getType() {
return type;
}
public String getDescription() {
return description;
}
public String getIpAddress() {
return ipAddress;
}
public void setType(String type) {
this.type = type;
}
public void setDescription(String description) {
this.description = description;
}
public void setIpAddress(String ipAddress) {
this.ipAddress = ipAddress;
}
public String toString() {
return "{\"type\":\"%s\",\"ipAddress\":\"%s\",\"description\":\"%s\"}".formatted(type, ipAddress, description);
}
}

View File

@ -0,0 +1,31 @@
package com.banesco.common.domain.model;
public class RedisParam {
private String prefix;
private long expiration;
public RedisParam() {
}
public RedisParam(String prefix, long expiration) {
this.prefix = prefix;
this.expiration = expiration;
}
public String getPrefix() {
return prefix;
}
public void setPrefix(String prefix) {
this.prefix = prefix;
}
public long getExpiration() {
return expiration;
}
public void setExpiration(long expiration) {
this.expiration = expiration;
}
}

View File

@ -0,0 +1,10 @@
package com.banesco.common.domain.model;
public record StatusResponse(String statusCode, String statusDesc) {
@Override
public String toString() {
return "{\"statusCode\":\"" + statusCode + "\",\"statusDesc\":\"" + statusDesc + "\"}";
}
}

View File

@ -0,0 +1,20 @@
package com.banesco.common.infraestructure.config;
public enum DeviceTypeValues {
Mobile,
Tablet,
PersonalComputer,
SecureCardReader,
NonSecureCardReader,
OtherNational,
OtherPrivate,
Wearable,
Vehicle,
Embedded,
KeyFob,
Jewellery,
Sticker,
GamingDevice,
Watch,
Unknown
}

View File

@ -0,0 +1,21 @@
package com.banesco.common.infraestructure.helpers;
/**
* Clase utilitaria para operaciones relacionadas con cuentas bancarias.
* Proporciona métodos para manipular y formatear información de cuentas.
*/
public class AccountHelper {
/**
* Enmascara un número de cuenta, mostrando solo los primeros 4
* y últimos 4 Los dígitos intermedios son reemplazados por asteriscos para proteger la
* información sensible del cliente.
*
* @param clearAccountId El número de cuenta completo sin enmascarar
* @return El número de cuenta enmascarado en formato "XXXX************YYYY"
*/
public static String getMaskedAccountId(String clearAccountId) {
return clearAccountId.substring(0, 4) + "************" + clearAccountId.substring(clearAccountId.length() - 4);
}
}

View File

@ -0,0 +1,112 @@
package com.banesco.common.infraestructure.helpers;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;
/**
* Clase de utilidad para operaciones comunes de fecha y hora.
* Utiliza el API moderno de Java (java.time).
*/
public final class DateHelper {
// Define la zona horaria de Venezuela una sola vez.
private static final ZoneId VENEZUELA_ZONE_ID = ZoneId.of("America/Caracas");
// Define los formatos de fecha y hora como constantes para reutilizarlos.
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm:ss");
// Instancia única para el patrón Singleton (compatibilidad con código existente)
private static final DateHelper INSTANCE = new DateHelper();
/**
* Constructor privado para prevenir que la clase sea instanciada directamente.
*/
private DateHelper() {
// Clase de utilidad, no necesita ser instanciada.
}
/**
* Obtiene la instancia única de DateHelper (patrón Singleton).
* @return La instancia única de DateHelper.
*/
public static DateHelper getInstance() {
return INSTANCE;
}
/**
* Obtiene la fecha y/o hora actual en la zona horaria de Venezuela.
*
* @param separate Un flag que indica si se deben devolver la fecha y la hora por separado.
* - Si es `false`, devuelve una sola cadena "yyyy-MM-dd HH:mm:ss".
* - Si es `true`, devuelve un Map con las llaves "date" y "time".
* @return Un objeto (String o Map) con la fecha y hora formateadas.
*/
public static Object getCurrentVenezuelanDateTime(boolean separate) {
// Obtiene la fecha y hora actual en la zona horaria especificada.
ZonedDateTime now = ZonedDateTime.now(VENEZUELA_ZONE_ID);
if (separate) {
// Devuelve las partes en un mapa
Map<String, String> dateTimeParts = new HashMap<>();
dateTimeParts.put("date", now.format(DATE_FORMATTER));
dateTimeParts.put("time", now.format(TIME_FORMATTER));
return dateTimeParts;
} else {
// Devuelve una sola cadena de texto combinada
return now.format(DATE_FORMATTER) + " " + now.format(TIME_FORMATTER);
}
}
/**
* Función de conveniencia para obtener solo la fecha actual formateada.
* @return La fecha actual como un String "yyyy-MM-dd".
*/
public static String getCurrentVenezuelanDate() {
return ZonedDateTime.now(VENEZUELA_ZONE_ID).format(DATE_FORMATTER);
}
/**
* Función de conveniencia para obtener solo la hora actual formateada.
* @return La hora actual como un String "HH:mm:ss".
*/
public static String getCurrentVenezuelanTime() {
return ZonedDateTime.now(VENEZUELA_ZONE_ID).format(TIME_FORMATTER);
}
/**
* Verifica si una hora está dentro de un rango especificado.
*
* @param hourBegin Hora de inicio en formato HHmm (ej: 800 para 08:00, 1800 para 18:00).
* @param hourFinish Hora de fin en formato HHmm (ej: 800 para 08:00, 1800 para 18:00).
* @param timeString Hora a verificar en formato "HH:mm:ss" o "HH:mm".
* @return true si la hora está dentro del rango [hourBegin, hourFinish], false en caso contrario.
*/
public boolean isHourInRange(int hourBegin, int hourFinish, String timeString) {
if (timeString == null || timeString.trim().isEmpty()) {
return false;
}
// Extrae las horas y minutos del String (formato "HH:mm:ss" o "HH:mm")
String[] timeParts = timeString.split(":");
if (timeParts.length < 2) {
return false;
}
try {
int hour = Integer.parseInt(timeParts[0]);
int minute = Integer.parseInt(timeParts[1]);
// Convierte la hora a formato HHmm
int currentHour = hour * 100 + minute;
// Verifica si está en el rango
return currentHour >= hourBegin && currentHour <= hourFinish;
} catch (NumberFormatException e) {
return false;
}
}
}

View File

@ -0,0 +1,154 @@
package com.banesco.common.infraestructure.helpers;
import com.banesco.common.domain.dto.bian.device.BianDevice;
import com.banesco.common.domain.model.Device;
import com.banesco.common.infraestructure.config.DeviceTypeValues;
import javax.servlet.http.HttpServletRequest;
import java.util.Objects;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class DeviceHelper {
private boolean readDeviceFromRequest;
private final String DESKTOP = "Desktop";
private static final Logger logger = Logger.getLogger(DeviceHelper.class.getName());
/**
* Nombres de cabeceras HTTP que pueden contener la dirección IP del
* cliente. Se revisan en orden para encontrar la IP real considerando
* proxies y balanceadores.
*/
private final String[] IP_HEADER_NAMES = {
"X-Forwarded-For",
"Proxy-Client-IP",
"WL-Proxy-Client-IP",
"HTTP_X_FORWARDED_FOR",
"HTTP_X_FORWARDED",
"HTTP_X_CLUSTER_CLIENT_IP",
"HTTP_CLIENT_IP",
"HTTP_FORWARDED_FOR",
"HTTP_FORWARDED",
"HTTP_VIA",
"REMOTE_ADDR"
};
public DeviceHelper() {
// Lee la propiedad del sistema. Si no existe, asume false por defecto.
String readDeviceProperty = System.getProperty("api.server-request.read-device");
this.readDeviceFromRequest = readDeviceProperty == null ? false : Boolean.parseBoolean(readDeviceProperty);
}
/**
* Obtiene la dirección IP remota del cliente a partir de la solicitud HTTP.
* Revisa varias cabeceras HTTP que podrían contener la IP real del cliente
* considerando proxies y balanceadores de carga.
*
* @param request La solicitud HTTP
* @return La dirección IP remota del cliente, o "0.0.0.0" si la solicitud
* es nula
*/
public String getRemoteIP(HttpServletRequest request) {
if (request == null) {
return "0.0.0.0";
}
for (String header : IP_HEADER_NAMES) {
String ipList = request.getHeader(header);
if (!StringHelper.isEmpty(ipList) && !"unknown".equalsIgnoreCase(ipList)) {
return ipList.split(",")[0];
}
}
return request.getRemoteAddr();
}
/**
* Llena la información del dispositivo remoto del cliente en el objeto
* Device. Si se configura readDeviceFromRequest como true, siempre se
* obtendrá la información del dispositivo desde la solicitud HTTP,
* independientemente de si ya existe.
*
* @param servletRequest La solicitud HTTP
* @param device El objeto Device existente a actualizar o null para crear
* uno nuevo
* @return El objeto Device con la información del cliente actualizada
*/
public Device fillRemoteDevice(HttpServletRequest servletRequest, Device device) {
if (device == null) {
device = new Device();
}
if (StringHelper.isEmpty(device.getIpAddress()) || readDeviceFromRequest) {
device.setIpAddress(getRemoteIP(servletRequest));
String userAgent = servletRequest.getHeader("user-agent");
if (userAgent == null) {
device.setType(DESKTOP);
device.setDescription(DESKTOP);
} else {
device.setDescription(userAgent);
if (userAgent.toLowerCase().contains("mobi")) {
device.setType("Mobile");
} else {
device.setType(DESKTOP);
}
}
}
return device;
}
/**
* Mapea el tipo de dispositivo desde el objeto BianDevice al enum DeviceTypeValues.
*
* @param device
* @return
*/
public static DeviceTypeValues getDeviceTypeValue(
BianDevice device
) {
var deviceTypeValue = DeviceTypeValues.Unknown;
if (!Objects.isNull(device) && !StringHelper.isEmpty(device.getDeviceType())) {
try {
deviceTypeValue = DeviceTypeValues.valueOf(device.getDeviceType());
} catch (Exception e) {
logger.log(Level.WARNING, "Error Mapping Device Type: %s -> %s".formatted(device.getDeviceType(), e.getMessage()));
}
}
return deviceTypeValue;
}
/**
* Valida si el tipo de dispositivo es válido según el enum DeviceTypeValues.
*
* @param type
* @return
*/
public static boolean isValidDeviceType(String type) {
for (DeviceTypeValues deviceType : DeviceTypeValues.values()) {
if (deviceType.name().equals(type)) {
return true;
}
}
return false;
}
/**
* Obtiene una cadena con todos los tipos de dispositivos válidos.
*
* @return
*/
public static String getDeviceTypes() {
return Stream.of(DeviceTypeValues.values()).map(Enum::name).collect(
Collectors.joining(", ")
);
}
}

View File

@ -0,0 +1,127 @@
package com.banesco.common.infraestructure.helpers;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
public class JsonHelper {
private static final Logger logger = Logger.getLogger(JsonHelper.class.getName());
public static final ObjectMapper MAPPER = new ObjectMapper();
static {
MAPPER.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
MAPPER.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
// Intentar registrar módulos automáticamente (incluye JavaTimeModule si está disponible)
try {
MAPPER.findAndRegisterModules();
} catch (Exception e) {
logger.log(Level.WARNING, "Error auto-registering Jackson modules, trying manual registration: {0}", e.getMessage());
// Fallback: intentar registro manual del JavaTimeModule
try {
MAPPER.registerModule(new JavaTimeModule());
} catch (Exception e2) {
logger.log(Level.SEVERE, "Error registering JavaTimeModule: {0}", e2.getMessage());
}
}
}
/**
* Convert Object to jsonString
*
* @param object El objeto a convertir en JSON.
* @return La cadena JSON resultante o null si ocurre un error.
*/
public static String getJsonFromObject(Object object) {
if (object == null) {
return "null";
}
try {
return MAPPER.writeValueAsString(object);
} catch (JsonProcessingException e) {
logger.log(Level.SEVERE, "Error parsing objeto a JSON: {0}", e.getMessage());
return null;
}
}
/**
* Convert Object to jsonString in compact format (single line, includes nulls)
*
* @param object El objeto a convertir en JSON.
* @return La cadena JSON compacta en una sola línea o null si ocurre un error.
*/
public static String getJsonFromObjectExcludingNulls(Object object) {
if (object == null) {
return "null";
}
try {
// Usar el formato compacto (sin pretty print) pero manteniendo todos los campos incluyendo nulls
return MAPPER.writeValueAsString(object);
} catch (JsonProcessingException e) {
logger.log(Level.SEVERE, "Error parsing objeto a JSON: {0}", e.getMessage());
return null;
}
}
/**
* Convierte una cadena JSON en un objeto Java de la clase especificada.
*
* @param json La cadena JSON a convertir.
* @param className La clase del objeto Java resultante.
* @param <T> El tipo del objeto resultante.
* @return El objeto Java resultante, o null si ocurre un error o la cadena
* JSON está vacía.
*/
public static <T> T getObjectFromJson(String json, Class<T> className) {
if (json == null || json.isEmpty()) {
return null;
}
try {
return MAPPER.readValue(json, className);
} catch (JsonProcessingException | IllegalArgumentException e) {
logger.log(Level.WARNING, "Error parsing JSON a objeto: {0}", e.getMessage());
return null;
}
}
public static <T> List<T> getListFromJson(String json, Class<T> className) {
if (json == null || json.isEmpty()) {
return new ArrayList<>();
}
try {
return MAPPER.readValue(json,
MAPPER.getTypeFactory().constructCollectionType(List.class, className)
);
} catch (JsonProcessingException | IllegalArgumentException e) {
logger.log(Level.WARNING, "Error parsing JSON a List Object: {0}", e.getMessage());
}
return new ArrayList<>();
}
public static <T> List<T> getListFromInputStream(InputStream inputStream, Class<T> className) throws IOException {
if (inputStream == null) {
return new ArrayList<>();
}
try {
return MAPPER.readValue(inputStream,
MAPPER.getTypeFactory().constructCollectionType(List.class, className)
);
} catch (JsonProcessingException | IllegalArgumentException e) {
logger.log(Level.WARNING, "Error parsing JSON a List Object: {0}", e.getMessage());
}
return new ArrayList<>();
}
}

View File

@ -0,0 +1,160 @@
package com.banesco.common.infraestructure.helpers;
import java.util.Map;
import java.util.stream.Collectors;
/**
* Clase utilitaria para la generación de mensajes de registro (logging).
* Facilita la creación de cadenas formateadas para registrar información de
* solicitudes y respuestas, tanto públicas como privadas, así como mensajes de
* error. Utiliza un formato estandarizado para mejorar la legibilidad y
* facilitar el análisis de los registros del sistema.
*/
public class LoggerHelper {
/**
* Formatea un mapa de headers en un String con un header por línea.
* Ejemplo: "Header1: valor1\nHeader2: valor2"
*
* @param headers El mapa de headers a formatear.
* @return Un String formateado y listo para ser logueado.
*/
public static String formatHeaders(Map<String, String> headers) {
if(headers == null) {
return "Headers: [vacios]";
}
return headers.entrySet().stream()
.map(entry -> entry.getKey() + ": " + entry.getValue())
.collect(Collectors.joining("\n", "\n Headers: \n", ""));
}
/**
* Construye una cadena de información para una solicitud pública.
*
* @param requestId El ID de la solicitud.
* @param request El objeto de solicitud.
* @param <T> El tipo del objeto de solicitud.
* @return Una cadena de información formateada.
*/
public static <T> String buildInfoRequest(String requestId, T request) {
return String.format("[PUB RQ: %s] %s", requestId, JsonHelper.getJsonFromObject(request));
}
/**
* Construye una cadena de información para una solicitud pública.
*
* @param requestId El ID de la solicitud.
* @param request El request en cadena de la solicitud.
* @return Una cadena de información formateada.
*/
public static <T> String buildInfoRequest(String requestId, String request) {
return String.format("[PUB RQ: %s] %s", requestId, request);
}
/**
* Construye una cadena de información para una respuesta pública.
*
* @param requestId El ID de la solicitud.
* @param response El objeto de respuesta.
* @param <T> El tipo del objeto de respuesta.
* @return Una cadena de información formateada.
*/
public static <T> String buildInfoResponse(String requestId, T response) {
return String.format("[PUB RS: %s] %s", requestId, JsonHelper.getJsonFromObject(response));
}
/**
* Construye una cadena de información para una respuesta pública.
*
* @param requestId El ID de la solicitud.
* @param response El objeto en cadena de respuesta.
* @return Una cadena de información formateada.
*/
public static String buildInfoResponse(String requestId, String response) {
return String.format("[PUB RS: %s] %s", requestId, response);
}
/**
* Construye una cadena de información para una solicitud privada.
*
* @param requestId El ID de la solicitud.
* @param name El nombre de la solicitud privada.
* @param request El objeto de solicitud.
* @param <T> El tipo del objeto de solicitud.
* @return Una cadena de información formateada.
*/
public static <T> String buildInfoPrivateRequest(String requestId, String name, T request) {
return String.format("[PRV RQ %s: %s] %s", requestId, name, JsonHelper.getJsonFromObject(request));
}
/**
* Construye una cadena de información para una solicitud privada.
*
* @param requestId El ID de la solicitud.
* @param request El objeto de solicitud.
* @return Una cadena de información formateada.
*/
public static String buildInfoPrivateRequest(String requestId, String request) {
return String.format("[PRV RQ: %s] %s", requestId, request);
}
/**
* Construye una cadena de información para una solicitud privada.
*
* @param requestId El ID de la solicitud.
* @param name El nombre de la solicitud privada.
* @param request El objeto en cadena de la solicitud.
* @return Una cadena de información formateada.
*/
public static String buildInfoPrivateRequest(String requestId, String name, String request) {
return String.format("[PRV RQ %s: %s] %s", requestId, name, request);
}
/**
* Construye una cadena de información para una respuesta privada.
*
* @param requestId El ID de la solicitud.
* @param name El nombre de la respuesta privada.
* @param response El objeto de respuesta.
* @param <T> El tipo del objeto de respuesta.
* @return Una cadena de información formateada.
*/
public static <T> String buildInfoPrivateResponse(String requestId, String name, T response) {
return String.format("[PRV RS %s: %s] %s", requestId, name, JsonHelper.getJsonFromObject(response));
}
/**
* Construye una cadena de información para una respuesta privada.
*
* @param requestId El ID de la solicitud.
* @param response El objeto en cadena de la respuesta.
* @return Una cadena de información formateada.
*/
public static String buildInfoPrivateResponse(String requestId, String response) {
return String.format("[PRV RS: %s] %s", requestId, response);
}
/**
* Construye una cadena de información para una respuesta privada.
*
* @param requestId El ID de la solicitud.
* @param name El nombre de la respuesta privada.
* @param response El objeto en cadena de la respuesta.
* @return Una cadena de información formateada.
*/
public static String buildInfoPrivateResponse(String requestId, String name, String response) {
return String.format("[PRV RS %s: %s] %s", requestId, name, response);
}
/**
* Construye una cadena de error.
*
* @param requestId El ID de la solicitud asociada al error.
* @param out El mensaje de error.
* @return Una cadena de error formateada.
*/
public static String buildError(String requestId, String out) {
return String.format("[ERROR %s] %s", requestId, out);
}
}

View File

@ -0,0 +1,15 @@
package com.banesco.common.infraestructure.helpers;
import com.banesco.common.domain.dto.bian.paymentCard.CardTypeValues;
public class PaymentCardHelper {
public static boolean isValidCardType(String type) {
for (CardTypeValues cardTypeValue : CardTypeValues.values()) {
if (cardTypeValue.name().equals(type)) {
return true;
}
}
return false;
}
}

View File

@ -0,0 +1,81 @@
package com.banesco.common.infraestructure.helpers;
import java.util.concurrent.ThreadLocalRandom;
/**
* Clase utilitaria para operaciones relacionadas con solicitudes HTTP y
* generación de identificadores. Proporciona métodos para generar IDs únicos,
* obtener información del cliente y gestionar información del dispositivo.
*/
public class RequestHelper {
private final static String ALPHANUMERIC_CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
private final static int baseCharacterLen = ALPHANUMERIC_CHARACTERS.length();
/**
* Permite tener una cadena aleatoria de caracteres numericos
*
* @param len longitud de la cadena numerica a retornar
* @return
*/
public String randomNumeric(int len) {
char[] chars = new char[len];
for (int i = 0; i < len; i++) {
chars[i] = (char) ThreadLocalRandom.current().nextInt(48, 58); // ascii index number
}
return new String(chars);
}
/**
* Permite tener una cadena aleatoria de caracteres alfa-numericos
*
* @param len longitud de la cadena alfa-numerica a retornar
* @return
*/
public static String randomAlphanumeric(int len) {
char[] chars = new char[len];
for (int i = 0; i < len; i++) {
chars[i] = ALPHANUMERIC_CHARACTERS.charAt(ThreadLocalRandom.current().nextInt(baseCharacterLen));
}
return new String(chars);
}
/**
* Retorna Identificador de la Instancia del Request, usando como prefijo un valor predefinido
* ejemplos: P2P, REM,
* Facilita identificar el origen de los Requests
* Deberia obtenerse una vez al inicia el Request
*
* @param apiSourceId
* @return
*/
public String getInstanceId(String apiSourceId) {
return (apiSourceId == null ? "INST" : apiSourceId).concat(randomAlphanumeric(10));
}
/**
* Retorna un Identificador del Request, se sugiere usar el getInstanceId creada como prefijo.
* Usar uno para Cada Request que implique afectacion financiera
*
* @param instanceId
* @return
*/
public static String getRequestId(String instanceId) {
return instanceId.concat("REQ").concat(randomAlphanumeric(20));
}
/**
* Permite Obtener un valor Numerico de Identificador de Transaccion
*
* @param len
* @return
*/
public String getTransactionId(int len) {
return randomNumeric(len);
}
}

View File

@ -0,0 +1,106 @@
package com.banesco.common.infraestructure.helpers;
/**
* Clase utilitaria para operaciones con cadenas de texto (strings). Proporciona
* métodos para validar, manipular y formatear cadenas de texto. Incluye
* funcionalidades como verificación de tipos numéricos, validación de cadenas
* no vacías y funciones de formato como relleno de caracteres.
*/
public class StringHelper {
/**
* Verifica si una cadena de texto representa un número entero.
*
* @param value La cadena de texto a verificar.
* @return true si la cadena es un número entero válido, false en caso
* contrario.
* @throws NumberFormatException Si el string no tiene el formato adeacuado
*/
public static boolean isNumeric(String value) {
if (value == null || value.isEmpty()) {
return false;
}
try {
Integer.parseInt(value);
return true;
} catch (NumberFormatException e) {
return false;
}
}
/**
* Verifica si una cadena de texto es nula o está vacía después de
* eliminar espacios en blanco.
*
* @param value La cadena de texto a verificar.
* @return true si la cadena está vacía, false en caso contrario.
*/
public static boolean isEmpty(String value) {
return value == null || value.trim().isEmpty();
}
/**
* Rellena una cadena de texto con un carácter específico a la izquierda
* hasta alcanzar la longitud deseada.
*
* @param inputStr La cadena de texto a rellenar.
* @param strLenOut La longitud deseada de la cadena resultante.
* @param padChar El carácter de relleno.
* @return La cadena rellenada con el carácter especificado a la izquierda.
*/
public static String leftPad(String inputStr, int strLenOut, char padChar) {
if (inputStr == null) {
inputStr = ""; // Handle null input
}
int padLength = strLenOut - inputStr.length();
if (padLength <= 0) {
return inputStr; // No padding needed
}
char[] paddedString = new char[padLength];
for (int i = 0; i < padLength; i++) {
paddedString[i] = padChar;
}
return new String(paddedString).concat(inputStr);
}
/**
* Permite convertir nombre de propiedad separada con (,-) to CamelCase
*
* @param inputStr Cadena de texto a convertir
* @param firstUpper Flag que indica si la primera letra es Mayuscula
* @return
*/
public static String convertToCamelCase(String inputStr, boolean firstUpper) {
StringBuilder result = new StringBuilder();
boolean capitalizeNext = firstUpper;
for (char ch : inputStr.toCharArray()) {
if (ch == '.' || ch == '-') {
capitalizeNext = true; // Next character should be capitalized
} else {
if (capitalizeNext) {
result.append(Character.toUpperCase(ch)); // Capitalize current character
capitalizeNext = false; // Reset flag
} else {
result.append(Character.toLowerCase(ch)); // Append in lowercase
}
}
}
return result.toString();
}
/**
* @param inputStr Cadena de texto a convertir en UpperCamelCase
* @return
*/
public static String convertToUpperCamelCase(String inputStr) {
return convertToCamelCase(inputStr, true);
}
}

View File

@ -0,0 +1,168 @@
package com.banesco.common.infraestructure.helpers;
import jakarta.xml.bind.JAXBContext;
import jakarta.xml.bind.JAXBElement;
import jakarta.xml.bind.JAXBException;
import jakarta.xml.bind.Marshaller;
import javax.xml.namespace.QName;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Clase de utilidad para trabajar con XML utilizando JAXB.
*/
public class XmlHelper {
private static final Logger logger = Logger.getLogger(XmlHelper.class.getName());
// Cache para las instancias de JAXBContext.
// La creación de JAXBContext es una operación costosa, por lo que cachearlas
// mejora significativamente el rendimiento en aplicaciones que procesan XML frecuentemente.
// Se utiliza ConcurrentHashMap para garantizar la seguridad en entornos con múltiples hilos (thread-safe).
private static final ConcurrentMap<Class<?>, JAXBContext> JAXB_CONTEXT_CACHE = new ConcurrentHashMap<>();
/**
* Constructor privado para evitar la instanciación de esta clase de utilidad.
*/
private XmlHelper() {
throw new UnsupportedOperationException("Esta es una clase de utilidad y no puede ser instanciada.");
}
/**
* Imprime el contenido XML de un objeto JAXBElement en el log.
* Utiliza JAXB para serializar el objeto a XML y lo formatea para una mejor legibilidad.
*
* @param element El JAXBElement que contiene el objeto a serializar a XML.
* @param traceId requestID.
* @throws JAXBException Si ocurre un error durante la serialización JAXB.
*/
public static void printXml(JAXBElement<?> element, String traceId) throws JAXBException {
// Verifica si el elemento es nulo para evitar NullPointerException
Objects.requireNonNull(element, "El JAXBElement no puede ser nulo.");
// Obtiene el tipo declarado del elemento para crear el contexto JAXB.
// Esto asegura que JAXB conozca la clase raíz del objeto que se va a serializar.
Class<?> declaredType = element.getDeclaredType();
// Crea una instancia de JAXBContext para el tipo declarado.
// JAXBContext es costoso de crear, en aplicaciones de alto rendimiento
// se suele cachear o reutilizar. Para este caso de log, es aceptable crearlo aquí.
JAXBContext ctx = JAXBContext.newInstance(declaredType);
// Crea un Marshaller, que es responsable de convertir objetos Java a XML.
Marshaller marshaller = ctx.createMarshaller();
// Configura la propiedad para que el XML de salida esté formateado (indentado).
// Esto hace que el XML sea mucho más legible en los logs.
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
// Configura la propiedad para que el XML no incluya la declaración XML (<?xml version="1.0" encoding="UTF-8" standalone="yes"?>).
// Esto puede ser útil si solo quieres el contenido XML puro en el log,
// pero a menudo es mejor dejarlo si quieres un XML completo y válido.
// marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
// Usa ByteArrayOutputStream para capturar la salida XML en memoria.
// Esto es necesario porque Marshaller escribe en un OutputStream,
// y necesitamos obtener el String resultante para el log.
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
// Realiza la serialización del JAXBElement al OutputStream.
marshaller.marshal(element, outputStream);
// Convierte el contenido del ByteArrayOutputStream a un String.
// Es crucial especificar la codificación (UTF-8 es lo más común)
// para asegurar que los caracteres se interpreten correctamente.
String xmlString = outputStream.toString(StandardCharsets.UTF_8.name());
// Loggea el XML generado.
logger.log(Level.INFO, traceId + " - XML del servicio SOAP:\n{0}", xmlString);
} catch (IOException e) {
// Manejo de excepción si hay un problema con el ByteArrayOutputStream (poco probable en este caso).
logger.log(Level.SEVERE, "Error al procesar el OutputStream para el XML", e);
}
}
/**
* Crea un JAXBElement y lo convierte a un String XML en un solo paso.
* <p>
* Este es un método de conveniencia de alto nivel que simplifica la generación de XML
* al encargarse de la creación del QName y del JAXBElement internamente.
*
* @param namespaceUri El URI del namespace para el elemento raíz XML.
* @param localPart El nombre local del elemento raíz XML (ej: "readCustomerProducts_Rq").
* @param declaredType El .class del objeto JAXB que se está serializando.
* @param value El objeto de datos JAXB (payload) que se va a convertir.
* @param <T> El tipo del objeto de datos.
* @return Un String con la representación XML formateada del objeto.
* @throws JAXBException Si ocurre algún error durante la serialización.
*/
public static <T> String toXml(String namespaceUri, String localPart, Class<T> declaredType, T value) throws JAXBException {
// 1. Crear el QName (Qualified Name) a partir del namespace y el nombre local.
QName qName = new QName(namespaceUri, localPart);
// 2. Crear el JAXBElement, que envuelve el objeto de datos con su información de esquema XML.
JAXBElement<T> jaxbElement = new JAXBElement<>(qName, declaredType, value);
// 3. Reutilizar el método original para hacer la conversión a String.
return toXml(jaxbElement);
}
/**
* Convierte un objeto JAXBElement genérico en una representación de String con formato XML.
* <p>
* Este método es genérico y puede manejar cualquier tipo de objeto contenido dentro de un JAXBElement.
* Utiliza una caché para las instancias de JAXBContext, optimizando el rendimiento al evitar
* la recreación costosa de estos objetos.
*
* @param element El objeto JAXBElement que se va a convertir a XML. No puede ser nulo.
* @param <T> El tipo del valor contenido en el JAXBElement.
* @return Un String que representa el objeto en formato XML, indentado para fácil lectura.
* @throws JAXBException Si ocurre un error durante el proceso de marshalling (conversión de objeto a XML).
* Esto puede suceder si el contexto JAXB no se puede crear para la clase dada
* o si hay un problema durante la serialización.
*/
public static <T> String toXml(JAXBElement<T> element) throws JAXBException {
if (element == null) {
// Es preferible devolver un string vacío o lanzar una excepción si el elemento es nulo.
// Para un helper, lanzar IllegalArgumentException es una buena práctica.
throw new IllegalArgumentException("El JAXBElement de entrada no puede ser nulo.");
}
// Obtiene la clase del objeto contenido en el JAXBElement (por ejemplo, ReadCustomerProductsRq.class).
Class<?> objectClass = element.getDeclaredType();
// Obtiene el JAXBContext de la caché o lo crea si no existe.
// computeIfAbsent es una operación atómica que asegura que el contexto se cree solo una vez por clase.
JAXBContext context = JAXB_CONTEXT_CACHE.computeIfAbsent(objectClass, clazz -> {
try {
return JAXBContext.newInstance(clazz);
} catch (JAXBException e) {
// Envuelve la excepción comprobada en una excepción de runtime para usarla dentro de la lambda.
throw new RuntimeException("Error al crear la instancia de JAXBContext para la clase: " + clazz.getName(), e);
}
});
// Crea un Marshaller a partir del contexto. El Marshaller es el que realiza la conversión.
Marshaller marshaller = context.createMarshaller();
// Configura la propiedad para que la salida XML esté formateada con indentación y saltos de línea.
// Esto es clave para que el XML sea legible en los logs.
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
// Se utiliza StringWriter para escribir la salida XML en un String.
StringWriter stringWriter = new StringWriter();
// Realiza la serialización del objeto JAXBElement y escribe el resultado en el StringWriter.
marshaller.marshal(element, stringWriter);
// Devuelve el contenido del StringWriter como un String.
return stringWriter.toString();
}
}

View File

@ -0,0 +1,111 @@
package com.banesco.common.infraestructure.repository;
import com.banesco.common.domain.model.BackResponse;
import com.banesco.common.infraestructure.helpers.JsonHelper;
import com.banesco.common.infraestructure.helpers.StringHelper;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
public class MessageRepository {
private static final Logger logger = Logger.getLogger(MessageRepository.class.getName());
/**
* Respuesta de error por defecto que se utiliza cuando no se encuentra un
* error específico.
*/
/**
* Mapa que almacena múltiples mapas de códigos de error, donde la clave
* principal es el nombre del archivo de mensajes.
*/
public final Map<String, Map<String, BackResponse>> mapErrorsMultiple = new HashMap<>();
/**
* Carga los mensajes de error desde un InputStream y los almacena en un
* mapa.
*
* @param inputStream El InputStream que contiene los datos JSON de los
* mensajes de error.
* @param messageFileKey La clave del archivo de mensajes, utilizada para
* almacenar el mapa en mapErrorsMultiple.
* @return Un mapa que contiene los códigos de error como claves y los
* objetos BackResponse como valores.
* @throws IOException Si ocurre un error de entrada/salida al leer el
* InputStream.
*/
private Map<String, BackResponse> loadFile(InputStream inputStream, String messageFileKey) throws IOException {
Map<String, BackResponse> mapStatusCodes = new HashMap<>();
Arrays.stream(JsonHelper.MAPPER.readValue(inputStream, BackResponse[].class)).forEach(o -> {
mapStatusCodes.put(o.getBackendCode(), o);
});
mapErrorsMultiple.put(messageFileKey, mapStatusCodes);
return mapStatusCodes;
}
/**
* Carga los mensajes de error desde una cadena JSON y los almacena en un
* mapa.
*
* @param messageFileKey La clave del archivo de mensajes, utilizada para
* almacenar el mapa en mapErrorsMultiple.
* @param jsonString La cadena JSON que contiene los datos de los mensajes
* de error.
* @throws IOException Si ocurre un error al procesar la cadena JSON.
*/
public void loadMessagesFromString(String messageFileKey, String jsonString) throws IOException {
Map<String, BackResponse> mapStatusCodes = new HashMap<>();
Arrays.stream(JsonHelper.MAPPER.readValue(jsonString, BackResponse[].class)).forEach(o -> {
mapStatusCodes.put(o.getBackendCode(), o);
});
mapErrorsMultiple.put(messageFileKey, mapStatusCodes);
}
/**
* Obtiene el mapa de códigos de error para un archivo de respuesta
* específico.
*
* @param responseFile El nombre del archivo de respuesta para el cual se
* desea obtener el mapa de errores.
* @return Un mapa que contiene los códigos de error como claves y los
* objetos BackResponse como valores, o null si no se encuentra el mapa.
*/
public Map<String, BackResponse> getErrorMap(String responseFile, boolean insideProperties) {
Map<String, BackResponse> errorMap = mapErrorsMultiple.get(responseFile);
if (insideProperties || (errorMap != null && !errorMap.isEmpty())) {
return errorMap;
}
String fileName = (StringHelper.isNumeric(responseFile) ? "vb".concat(responseFile) : responseFile).concat(".json");
try {
try {
// Intenta cargar el archivo desde el directorio /config/.
return loadFile(new FileInputStream("/config/".concat(fileName)), responseFile);
} catch (IOException e) {
// Si no se encuentra el archivo en /config/, intenta cargarlo desde el classpath.
logger.log(Level.WARNING, "Path /config/" + fileName + " not found. Loading local from errors-mapping/" + fileName);
ClassLoader classLoader = getClass().getClassLoader();
InputStream inputStream = classLoader.getResourceAsStream("errors-mapping/" + fileName);
if (inputStream == null) {
// Si no se encuentra el archivo en el classpath, registra un error y devuelve null.
logger.log(Level.SEVERE, "Resource errors-mapping/" + fileName + " not found.");
return null;
}
return loadFile(inputStream, responseFile);
}
} catch (IOException e) {
// Si ocurre un error al cargar el archivo desde cualquier ubicación, registra un error y devuelve null.
logger.log(Level.SEVERE, "Error getting Service Messages to /config/".concat(fileName) + ": " + e.getMessage());
return null;
}
}
}

View File

@ -0,0 +1,90 @@
package com.banesco.common.infraestructure.repository;
import com.banesco.common.domain.model.ApiConfig;
import com.banesco.common.infraestructure.helpers.JsonHelper;
import com.banesco.common.infraestructure.helpers.LoggerHelper;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Level;
import java.util.logging.Logger;
public class RegisterSecurityRepository {
private static final Logger logger = Logger.getLogger(RegisterSecurityRepository.class.getName());
private final LoggerHelper loggerHelper;
private final ApiConfig registerSecurityAPI;
private final ExecutorService executor = Executors.newCachedThreadPool();
public RegisterSecurityRepository(LoggerHelper loggerHelper, ApiConfig registerSecurityAPI) {
this.loggerHelper = loggerHelper;
this.registerSecurityAPI = registerSecurityAPI;
}
public <T> void writeTrice(String requestId, T registerSecurityRq) {
logger.info(loggerHelper.buildInfoPrivateRequest(requestId, "RegisterSecurityRq", registerSecurityRq));
CompletableFuture.runAsync(() -> {
try {
URL url = new URL(registerSecurityAPI.getUrl());
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type", "application/json");
connection.setConnectTimeout(registerSecurityAPI.getTimeout().getConnect());
connection.setReadTimeout(registerSecurityAPI.getTimeout().getResponse());
connection.setDoOutput(true);
String jsonInputString = JsonHelper.getJsonFromObject(registerSecurityRq);
try (OutputStream os = connection.getOutputStream()) {
byte[] input = jsonInputString.getBytes(StandardCharsets.UTF_8);
os.write(input, 0, input.length);
}
int responseCode = connection.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
try (BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8))) {
StringBuilder response = new StringBuilder();
String responseLine;
while ((responseLine = br.readLine()) != null) {
response.append(responseLine.trim());
}
logger.info(loggerHelper.buildInfoPrivateResponse(requestId, "RegisterSecurityRs", response.toString()));
}
} else {
try (BufferedReader br = new BufferedReader(new InputStreamReader(connection.getErrorStream(), StandardCharsets.UTF_8))) {
StringBuilder response = new StringBuilder();
String responseLine;
while ((responseLine = br.readLine()) != null) {
response.append(responseLine.trim());
}
logger.info(loggerHelper.buildInfoPrivateResponse(requestId, "RegisterSecurityRs", response.toString()));
}
}
connection.disconnect();
} catch (IOException e) {
logger.log(Level.SEVERE, String.format("Error interno writeTrice: %s", e.getMessage()));
logger.info(loggerHelper.buildInfoPrivateResponse(requestId, "RegisterSecurityRs", e.getMessage()));
}
}, executor).exceptionally(ex -> {
logger.log(Level.SEVERE, String.format("Async Error interno writeTrice: %s", ex.getMessage()));
logger.info(loggerHelper.buildInfoPrivateResponse(requestId, "RegisterSecurityRs", ex.getMessage()));
return null;
});
}
public void shutdown() {
executor.shutdown();
}
}

View File

@ -0,0 +1,113 @@
package com.banesco.common.infraestructure.service;
import com.banesco.common.domain.dto.bian.response.BianStatusResponse;
import com.banesco.common.domain.model.BackResponse;
import com.banesco.common.domain.model.StatusResponse;
import com.banesco.common.infraestructure.repository.MessageRepository;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
public class MessageService {
private static final Logger logger = Logger.getLogger(MessageService.class.getName());
private final MessageRepository messageRepository;
private final String CONFLICT = "CONFLICT";
private final BackResponse DEFAULT_ERROR = new BackResponse(
CONFLICT,
409,
CONFLICT,
CONFLICT
);
private boolean messagesInsideProperties = false;
/**
* Constructor de MessageService.
*
* @param messageRepository El repositorio de mensajes que se utilizará para
* cargar y obtener mensajes de error.
*/
public MessageService(MessageRepository messageRepository) {
this.messageRepository = messageRepository;
}
/**
* Carga mensajes desde una cadena JSON en el repositorio de mensajes.
*
* @param responseFileName El nombre del archivo de respuesta asociado con
* los mensajes.
* @param jsonResponse La cadena JSON que contiene los mensajes.
*/
public void loadMessageFromJson(String responseFileName, String jsonResponse) {
messagesInsideProperties = true;
try {
messageRepository.loadMessagesFromString(responseFileName, jsonResponse);
} catch (Exception ex) {
logger.log(Level.SEVERE, "Error loading messages from JSON: %s".formatted(ex.getMessage()), ex);
}
}
/**
* Obtiene una respuesta de estado (BackResponse) basada en el código de
* backend y el nombre del archivo de respuesta.
*
* @param responseFileName El nombre del archivo de respuesta.
* @param backendCode El código de backend para buscar la respuesta.
* @return La respuesta de estado (BackResponse) correspondiente al código
* de backend, la respuesta por defecto si no se encuentra, o null si hay un
* error.
*/
public BackResponse geStatusResponse(String responseFileName, String backendCode) {
logger.log(Level.INFO, "Finding backendCode: \"%s\" in File: \"%s\"".formatted(backendCode, responseFileName));
Map<String, BackResponse> errors = messageRepository.getErrorMap(responseFileName, messagesInsideProperties);
if (errors == null || errors.isEmpty()) {
logger.log(Level.WARNING, "Error map for %s is empty or null.".formatted(responseFileName));
return DEFAULT_ERROR;
}
BackResponse backResponse = null;
try {
backResponse = errors.get(backendCode);
if (backResponse == null) {
backResponse = errors.get("default");
}
} catch (Exception e) {
logger.log(Level.WARNING, "Error code: %s not found in %s. Using default.".formatted(backendCode, responseFileName), e);
try {
backResponse = errors.get("default");
} catch (Exception e2) {
logger.log(Level.SEVERE, "Error getting default code.", e2);
}
}
if (backResponse == null) {
logger.log(Level.WARNING, "Error code: %s and default not found in %s.".formatted(backendCode, responseFileName));
return DEFAULT_ERROR;
}
return backResponse;
}
public StatusResponse backToStatusResponse(BackResponse backResponse, String target) {
return new StatusResponse(
backResponse.getStatusCode(),
target == null ? backResponse.getDescription() :
backResponse.getDescription().formatted(target)
);
}
public BianStatusResponse backToBianStatusResponse(String traceId, BackResponse backResponse, String target) {
return new BianStatusResponse(
backResponse.getHttpCode() >= 200 && backResponse.getHttpCode() < 300 ? "ok" : "error",
backResponse.getStatusCode(),
target == null ? backResponse.getDescription() :
backResponse.getDescription().formatted(target),
traceId
);
}
}

View File

@ -0,0 +1,90 @@
package com.banesco.common.infraestructure.utils;
import java.time.DateTimeException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
/**
* Clase para validación de fechas con configuración flexible. Permite validar
* fechas en diferentes formatos, comprobar rangos de años y verificar la
* correcta formación de componentes de fecha (año, mes, día). Implementa el
* patrón Builder para facilitar la configuración personalizada de los
* parámetros de validación.
*/
public class DateValidator {
private final DateTimeFormatter dateFormatter;
private final PatternValidator datePattern;
private final int minYear;
private final int maxYear;
private DateValidator(Builder builder) {
this.dateFormatter = builder.dateFormatter;
this.datePattern = new PatternValidator(builder.dateFormatRegex);
this.minYear = builder.minYear;
this.maxYear = builder.maxYear;
}
public static class Builder {
private DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy/MM/dd");
private String dateFormatRegex = "(\\d{4})/(0[1-9]|1[0-2])/(0[1-9]|[12][0-9]|3[01])";
private int minYear = 1900;
private int maxYear = 2100;
public Builder withDateFormatter(String dateFormatterPattern) {
this.dateFormatter = DateTimeFormatter.ofPattern(dateFormatterPattern);
return this;
}
public Builder withDateFormatRegex(String dateFormatRegex) {
this.dateFormatRegex = dateFormatRegex;
return this;
}
public Builder withMinYear(int minYear) {
this.minYear = minYear;
return this;
}
public Builder withMaxYear(int maxYear) {
this.maxYear = maxYear;
return this;
}
public DateValidator build() {
return new DateValidator(this);
}
}
public boolean isValidDateFormat(String date) {
return datePattern.isValid(date);
}
public boolean isValidDate(String date) {
if (!isValidDateFormat(date)) {
return false;
}
try {
LocalDate parsedDate = LocalDate.parse(date, dateFormatter);
String formattedDate = parsedDate.format(dateFormatter);
return date.equals(formattedDate);
} catch (DateTimeException e) {
return false;
}
}
public boolean isValidDate(int year, int month, int day) {
return year >= minYear && year <= maxYear && month >= 1 && month <= 12 && isValidDayOfMonth(year, month, day);
}
private boolean isValidDayOfMonth(int year, int month, int day) {
try {
LocalDate.of(year, month, day);
return true;
} catch (DateTimeException e) {
return false;
}
}
}

View File

@ -0,0 +1,32 @@
package com.banesco.common.infraestructure.utils;
import java.util.regex.Pattern;
/**
* Clase para validar Expresion regulares
*/
public class PatternValidator {
private final Pattern pattern;
public PatternValidator(String patternStr) {
pattern = Pattern.compile(patternStr);
}
public static PatternValidator withRegex(String patternStr) {
return new PatternValidator(patternStr);
}
/**
* Validates is un valor tiene formato valido.
*
* @param value the value to check.
* @return true if the value has format valid, false otherwise.
*/
public boolean isValid(String value) {
if (value == null) {
return false;
}
return pattern.matcher(value).matches();
}
}

View File

@ -0,0 +1,19 @@
package com.banesco;
/**
* Unit test for simple App.
*/
public class AppTest {
/**
* Create the test case
*
* @param testName name of the test case
*/
public AppTest(String testName) {
}
}