PI Function Commons
1. Descripción general
pi-function-commons es una librería JAR de utilidades compartidas consumida como dependencia Maven por el resto de microservicios del ecosistema Hawkers. No expone ningún endpoint HTTP ni arranca un servidor embebido: su único propósito es centralizar operaciones recurrentes que de otro modo se replicarían en cada servicio.
Agrupa las siguientes áreas funcionales:
- Generación y manipulación de PDFs (HTML→PDF, fusión, conversión a imagen).
- Operaciones FTP y SFTP (carga, descarga, listado, renombrado, gestión de directorios).
- Integración con Google Cloud Storage y Google Sheets API.
- Envío de correos electrónicos (texto plano, HTML, adjuntos).
- Formateo y validación de fechas con soporte de zonas horarias.
- Validación y normalización de números de teléfono.
- Transliteración Unicode a ASCII.
- Serialización JSON y XML.
- Gestión del sistema de ficheros local (directorios y archivos).
- Validaciones de códigos postales portugueses de islas.
2. Información técnica
| Propiedad | Valor |
|---|---|
artifactId | pi-function-commons |
groupId | com.hawkersco |
version | 1.0.25-SNAPSHOT |
| Java | 25 |
| Spring Boot | 4.0.6 |
| Tipo de artefacto | JAR (librería, sin capa web) |
| Módulos | Proyecto simple (no multi-módulo) |
| Repositorio | Google Artifact Registry (europe-west3-maven.pkg.dev/pi-saldum/pi-repo-maven) |
3. Arquitectura y diseño
La librería se organiza en dos paquetes bajo com.hawkersco.pifunctioncommons:
utils/ — Clases utilitarias estáticas
Ninguna es instanciable (constructor privado que lanza UnsupportedOperationException). No usan inyección de Spring.
| Clase | Responsabilidad |
|---|---|
PdfUtils | HTML→PDF vía Thymeleaf + JTidy + Flying Saucer + iText5; fusión de PDFs (PDFBox); conversión PDF→PNG/JPG con rotación opcional. |
DateUtils | Formateo y parseo de fechas con zonas horarias; conversión a XMLGregorianCalendar; timestamp UTC; operaciones aritméticas sobre fechas. Zona horaria por defecto: Europe/Madrid. |
MailUtils | Envío de emails: texto plano (uno o varios destinatarios), HTML, con adjunto único o múltiple, o destinatarios múltiples con adjunto. |
SeveralUtils | Transliteración Unicode→ASCII (ICU4J); formateo de teléfono a E.164; normalización de prefijos duplicados; descarga de ficheros por URL (java.net.http.HttpClient); detección de SKUs Northweek. |
StorageUtils | Carga y lectura de ficheros en Google Cloud Storage (texto, PDF, cualquier File). Variantes con y sin tipo MIME, con retorno booleano. |
ZipUtils | Descompresión de ZIPs (dos variantes: File y ruta String); búsqueda de PDFs en directorio; codificación Base64 de ficheros. |
JsonUtils | Serialización de listas de objetos a fichero JSON con Jackson ObjectMapper. |
SheetsServiceUtils | Inicialización de cliente autenticado de Google Sheets API v4 mediante Application Default Credentials. |
FtpUtils | Conexión FTP (Commons Net), subida de ficheros, desconexión. Modo binario + pasivo. Traza FTP a través de LoggerOutputStream. |
SftpUtils | Operaciones SFTP completas: conectar/desconectar (múltiples variantes incluyendo Properties extra), subir (fichero, boolean, directorio), descargar (primero encontrado, por nombre, por lista, directorio completo recursivo), listar (como lista, como mapa sku→fichero, con/sin extensión), buscar por regex, renombrar, eliminar, cargar XMLs y mover tras lectura, gestión de directorios remotos. |
PhoneNumberValidator | Validación de número para un país ISO; formateo INTERNATIONAL; limpieza de prefijo duplicado. |
ZipCodeUtils | Detecta si un código postal portugués pertenece a Madeira (9000–9400) o Azores (9500–9980). |
DirectoryUtils | Crear/eliminar directorios; guardar contenido en fichero; listar ficheros (con y sin extensión, en mayúsculas). |
LoggerOutputStream | Adaptador OutputStream → JUL Logger: bufferiza bytes y emite una entrada de log por línea. Usado por FtpUtils para trazar comandos FTP. |
PiFunctionCommonsCons | Constantes compartidas de la librería (p.ej. TXT_TIMEZONE_MAD = "Europe/Madrid"). |
services/ — Beans Spring
| Clase | Anotación principal | Responsabilidad |
|---|---|---|
SftpService | @Service | Gestión de sesión SFTP stateful: descarga todos los XMLs del directorio remoto configurado; renombrado en lote. Cada operación abre y cierra su propia sesión JSch. |
SftpProperties | @Configuration + @ConfigurationProperties(prefix = "sftp") + Lombok @Data | Externaliza la configuración SFTP del servicio consumidor. |
Diagrama de flujo — generación de PDF
flowchart LR
A[Template Thymeleaf] -->|process| B[HTML String]
B -->|JTidy| C[XHTML String]
C -->|ITextRenderer Flying Saucer| D[PDF en disco]
D -->|PDFMergerUtility| E[PDF fusionado]
E -->|PDFRenderer PDFBox| F[PNG / JPG]
Diagrama de flujo — descarga SFTP con SftpService
flowchart LR
A[SftpService.downloadFiles] --> B[JSch Session.connect]
B --> C[ChannelSftp.ls remoteDir]
C --> D{¿filename.endsWith .xml?}
D -- sí --> E[channelSftp.get → FileOutputStream local]
D -- no --> C
E --> F[disconnect channel + session]
F --> G[Lista de nombres descargados]
4. Dependencias principales
| Dependencia | Versión | Propósito |
|---|---|---|
spring-boot-starter | 4.0.6 (gestionado por parent) | Núcleo de Spring Boot |
spring-boot-starter-mail | 4.0.6 | JavaMailSender para envío de emails |
spring-boot-starter-thymeleaf | 4.0.6 | Motor de plantillas para HTML→PDF |
com.itextpdf:itextpdf | 5.5.13.5 | Generación de PDFs (iText5, requerido por Flying Saucer) |
net.sf.jtidy:jtidy | r938 | Conversión HTML→XHTML previa al renderizado PDF |
org.xhtmlrenderer:flying-saucer-core | 9.7.2 | Motor de renderizado XHTML |
org.xhtmlrenderer:flying-saucer-pdf-itext5 | 9.7.2 | Puente Flying Saucer → iText5 |
org.apache.pdfbox:pdfbox | 3.0.7 | Fusión de PDFs y conversión a imagen |
com.google.auth:google-auth-library-oauth2-http | 1.47.0 | Credenciales OAuth2 para servicios Google |
com.google.apis:google-api-services-sheets | v4-rev20250603-2.0.0 | Cliente Google Sheets API v4 |
com.google.cloud:google-cloud-storage | 2.68.0 | Cliente Google Cloud Storage |
com.github.mwiede:jsch | 0.2.25 | Cliente SFTP/SSH (fork mantenido de com.jcraft:jsch, abandonado desde 2018) |
commons-net:commons-net | 3.13.0 | Cliente FTP (Apache Commons Net) |
commons-io:commons-io | 2.22.0 | Utilidades de I/O (lectura de ficheros, Base64) |
com.ibm.icu:icu4j | 78.3 | Transliteración Unicode a ASCII |
com.fasterxml.jackson.core:jackson-databind | gestionado por parent | Serialización/deserialización JSON |
com.googlecode.libphonenumber:libphonenumber | 9.0.30 | Validación y formateo de números de teléfono |
com.google.guava:guava | 33.6.0-jre | Colecciones y utilidades Google |
org.json:json | 20251224 | Parseo JSON básico |
org.glassfish.jaxb:jaxb-runtime | gestionado por parent | JAXB para marshaling/unmarshaling XML |
org.projectlombok:lombok | 1.18.46 (provided) | Reducción de boilerplate (@Data en SftpProperties) |
Nota sobre Lombok: al ser
provided, los proyectos consumidores que utilicen clases anotadas de esta librería deben declarar también Lombok en su propiopom.xml. Además, Lombok debe aparecer en<annotationProcessorPaths>delmaven-compiler-plugin(requerido a partir del plugin 3.13+, que Spring Boot 4 incluye).
5. API / Endpoints
No aplica a este proyecto. pi-function-commons es una librería JAR sin capa web; no expone endpoints HTTP.
6. Integraciones externas
| Sistema externo | Protocolo / Cliente | Dirección del flujo | Clase responsable |
|---|---|---|---|
| Servidor FTP genérico | FTP (Commons Net, modo binario pasivo) | Saliente (upload) | FtpUtils |
| Servidor SFTP genérico | SFTP/SSH (JSch fork mwiede) | Bidireccional (upload/download) | SftpUtils, SftpService |
| Google Cloud Storage | HTTP/REST (SDK oficial) | Bidireccional | StorageUtils |
| Google Sheets API v4 | HTTP/REST (Google API Java client) | Saliente (lectura/escritura por el consumidor) | SheetsServiceUtils |
| Servidor de correo SMTP | SMTP (Spring Mail / Jakarta Mail) | Saliente (envío) | MailUtils |
| URL HTTP/HTTPS externas | HTTP (java.net.http.HttpClient) | Entrante (descarga de ficheros) | SeveralUtils.downloadFile |
| Google Artifact Registry | HTTPS / Maven Wagon | Saliente (publicación del artefacto) | pom.xml distributionManagement |
7. Configuración
La única configuración externalizable que gestiona la librería por sí misma es la del módulo SftpService. El resto de utilidades reciben sus parámetros de conexión directamente como argumentos en cada llamada de método.
Propiedades SFTP (SftpProperties)
Los proyectos consumidores que inyecten SftpService deben declarar estas propiedades en su application.yml:
| Clave | Descripción | Ejemplo de valor |
|---|---|---|
sftp.host | Hostname o IP del servidor SFTP | sftp.ejemplo.com |
sftp.port | Puerto del servidor SFTP | 22 |
sftp.user | Usuario para autenticación SFTP | usuario_sftp |
sftp.password | Contraseña SFTP (sensible) | ******** |
sftp.remoteDir | Directorio remoto de trabajo por defecto | /inbox/pedidos/ |
Ejemplo de configuración en el servicio consumidor:
sftp:
host: ${SFTP_HOST}
port: ${SFTP_PORT:22}
user: ${SFTP_USER}
password: ${SFTP_PASSWORD}
remote-dir: ${SFTP_REMOTE_DIR:/inbox/}
Google Cloud Storage / Sheets
StorageUtils y SheetsServiceUtils utilizan el objeto Bucket (GCS) o Application Default Credentials (Sheets), que deben gestionarse e inyectarse desde el servicio consumidor. La variable de entorno estándar de Google es:
| Variable de entorno | Descripción |
|---|---|
GOOGLE_APPLICATION_CREDENTIALS | Ruta al fichero JSON de cuenta de servicio Google |
Fuente de la fuente tipográfica PDF
PdfUtils.createPdf espera el fichero Montserrat-Regular.ttf en el classpath bajo /templates/Montserrat-Regular.ttf. Los proyectos consumidores deben incluir dicho recurso.
8. Persistencia
No aplica a este proyecto. La librería no accede a ninguna base de datos ni gestiona entidades persistentes.
9. Procesos programados y mensajería
No aplica a este proyecto. No hay anotaciones @Scheduled, listeners de colas (@KafkaListener, @RabbitListener) ni runners batch.
10. Ejecución en local
Requisitos previos
- JDK 25
- Maven 3.9+ (o usar el wrapper
./mvnw) - Acceso a Google Artifact Registry para publicar (requiere configuración de
~/.m2/settings.xmlcon credenciales o Application Default Credentials)
Compilar e instalar en repositorio Maven local
# Compilar sin tests e instalar en ~/.m2/local
./mvnw clean install -B -DskipTests
# Compilar con tests (actualmente no hay tests implementados)
./mvnw clean install
Usar la librería como dependencia
<dependency>
<groupId>com.hawkersco</groupId>
<artifactId>pi-function-commons</artifactId>
<version>1.0.25-SNAPSHOT</version>
</dependency>
Al ser una librería sin servidor embebido, no hay endpoint de salud ni puerto que verificar. La comprobación se limita a que el artefacto esté disponible en el repositorio y que el proyecto consumidor compile correctamente.
11. Despliegue
El despliegue consiste en publicar el JAR en Google Artifact Registry (europe-west3-maven.pkg.dev/pi-saldum/pi-repo-maven).
Pipeline Jenkins
https://jenkins-pi.hawkersco.net/job/pi-function-commons/
El Jenkinsfile define dos etapas:
| Etapa | Descripción |
|---|---|
Checkout | Obtiene el código fuente del repositorio (checkout scm) |
Publish to Artifact Registry | Ejecuta mvn deploy -DskipTests, que compila y publica el JAR en Artifact Registry |
Herramientas configuradas en Jenkins: JDK25 y Maven3.
No hay etapas de escaneado KICS/SonarQube en el
Jenkinsfileactual, a diferencia de otros proyectos del ecosistema PI.
12. Manejo de errores y logging
Estrategia de excepciones
- Las clases utilitarias no tienen manejo centralizado de errores: cada método declara las excepciones que puede lanzar (
IOException,JSchException,SftpException,JAXBException, etc.) para que el servicio consumidor decida cómo tratarlas. - Excepción:
MailUtils,FtpUtils,StorageUtilsySftpServicecapturan internamente las excepciones y las registran con el logger. Los errores no se propagan al llamador en estos casos. SftpUtils.connectyconnectSessionlanzanIllegalAccessExceptionsi ya hay una sesión activa, forzando al consumidor a gestionar el ciclo de vida de la sesión explícitamente.
Logging
- Todas las clases usan Java Util Logging (JUL) (
java.util.logging.Logger), no SLF4J/Logback. - Los niveles usados son
INFO,WARNINGySEVERE. FtpUtilsregistra todos los comandos FTP mediantePrintCommandListener+LoggerOutputStreama nivelINFO.SftpUtilsregistra descargas, subidas y errores con el path del fichero afectado.- El formato y destino final del log depende de la configuración del servicio consumidor.
13. Notas y consideraciones
Estado estático en SftpUtils y FtpUtils
SftpUtils mantiene un campo private static Session session y FtpUtils un private static FTPClient ftpClient. Esto hace que ambas clases sean no thread-safe si múltiples hilos las usan concurrentemente. En aplicaciones Spring Boot con múltiples hilos (p.ej. scheduled tasks paralelas o procesamiento de colas), se recomienda usar SftpService (que crea una sesión por llamada) o gestionar el ciclo de vida externamente.
StrictHostKeyChecking=no en SFTP
Tanto SftpUtils como SftpService deshabilitan la verificación de la clave del host SSH. Esto es conveniente en entornos controlados pero supone un riesgo de seguridad (susceptible a ataques man-in-the-middle). En entornos de producción expuestos a redes no confiables se debería añadir la clave del host a un known_hosts y configurar StrictHostKeyChecking=yes.
Nombre engañoso en DateUtils.getDateToDaysTimezone
El parámetro timezone de getDateToDaysTimezone(int days, String timezone) se usa internamente como patrón de formato de fecha, no como identificador de zona horaria. El nombre del parámetro es incorrecto y puede inducir a confusión.
Comportamiento no obvio en DateUtils.getCurrentDateWithZeroTime
A pesar de su nombre, este método no devuelve la fecha actual a las 00:00:00. Devuelve ayer a las -12:00 horas (usa calendar.set(Calendar.HOUR, -12), que equivale a las 12:00 PM del día anterior). Hay que revisarlo antes de usarlo en lógica de negocio que dependa de rangos de fechas exactos.
Ausencia de tests
src/test/java/ existe pero está vacío. No hay cobertura de tests automatizados para ninguna de las utilidades.
Application Default Credentials para Google Sheets
SheetsServiceUtils.getSheetsService() llama a GoogleCredentials.getApplicationDefault(), que requiere que la variable de entorno GOOGLE_APPLICATION_CREDENTIALS apunte a un fichero JSON de cuenta de servicio válido, o que el entorno de ejecución tenga credenciales ADC configuradas (p.ej. instancia GCE con service account).
Lote máximo en SftpUtils.getAllFilesNameFtp
El método limita el resultado a 10 ficheros por llamada (MAX_BATCH_FILES = 10). Los consumidores que procesen más de 10 ficheros por ciclo deben tenerlo en cuenta e implementar paginación o llamadas iterativas.
Timeout de sesión en SftpUtils.connect
connect configura un timeout de sesión de 3.600.000 ms (1 hora). connectSession y las demás variantes no configuran timeout, lo que puede resultar en sesiones colgadas indefinidamente si el servidor SFTP deja de responder.