Skip to main content

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

PropiedadValor
artifactIdpi-function-commons
groupIdcom.hawkersco
version1.0.25-SNAPSHOT
Java25
Spring Boot4.0.6
Tipo de artefactoJAR (librería, sin capa web)
MódulosProyecto simple (no multi-módulo)
RepositorioGoogle 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.

ClaseResponsabilidad
PdfUtilsHTML→PDF vía Thymeleaf + JTidy + Flying Saucer + iText5; fusión de PDFs (PDFBox); conversión PDF→PNG/JPG con rotación opcional.
DateUtilsFormateo y parseo de fechas con zonas horarias; conversión a XMLGregorianCalendar; timestamp UTC; operaciones aritméticas sobre fechas. Zona horaria por defecto: Europe/Madrid.
MailUtilsEnvío de emails: texto plano (uno o varios destinatarios), HTML, con adjunto único o múltiple, o destinatarios múltiples con adjunto.
SeveralUtilsTransliteració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.
StorageUtilsCarga y lectura de ficheros en Google Cloud Storage (texto, PDF, cualquier File). Variantes con y sin tipo MIME, con retorno booleano.
ZipUtilsDescompresión de ZIPs (dos variantes: File y ruta String); búsqueda de PDFs en directorio; codificación Base64 de ficheros.
JsonUtilsSerialización de listas de objetos a fichero JSON con Jackson ObjectMapper.
SheetsServiceUtilsInicialización de cliente autenticado de Google Sheets API v4 mediante Application Default Credentials.
FtpUtilsConexión FTP (Commons Net), subida de ficheros, desconexión. Modo binario + pasivo. Traza FTP a través de LoggerOutputStream.
SftpUtilsOperaciones 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.
PhoneNumberValidatorValidación de número para un país ISO; formateo INTERNATIONAL; limpieza de prefijo duplicado.
ZipCodeUtilsDetecta si un código postal portugués pertenece a Madeira (9000–9400) o Azores (9500–9980).
DirectoryUtilsCrear/eliminar directorios; guardar contenido en fichero; listar ficheros (con y sin extensión, en mayúsculas).
LoggerOutputStreamAdaptador OutputStream → JUL Logger: bufferiza bytes y emite una entrada de log por línea. Usado por FtpUtils para trazar comandos FTP.
PiFunctionCommonsConsConstantes compartidas de la librería (p.ej. TXT_TIMEZONE_MAD = "Europe/Madrid").

services/ — Beans Spring

ClaseAnotación principalResponsabilidad
SftpService@ServiceGestió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 @DataExternaliza 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

DependenciaVersiónPropósito
spring-boot-starter4.0.6 (gestionado por parent)Núcleo de Spring Boot
spring-boot-starter-mail4.0.6JavaMailSender para envío de emails
spring-boot-starter-thymeleaf4.0.6Motor de plantillas para HTML→PDF
com.itextpdf:itextpdf5.5.13.5Generación de PDFs (iText5, requerido por Flying Saucer)
net.sf.jtidy:jtidyr938Conversión HTML→XHTML previa al renderizado PDF
org.xhtmlrenderer:flying-saucer-core9.7.2Motor de renderizado XHTML
org.xhtmlrenderer:flying-saucer-pdf-itext59.7.2Puente Flying Saucer → iText5
org.apache.pdfbox:pdfbox3.0.7Fusión de PDFs y conversión a imagen
com.google.auth:google-auth-library-oauth2-http1.47.0Credenciales OAuth2 para servicios Google
com.google.apis:google-api-services-sheetsv4-rev20250603-2.0.0Cliente Google Sheets API v4
com.google.cloud:google-cloud-storage2.68.0Cliente Google Cloud Storage
com.github.mwiede:jsch0.2.25Cliente SFTP/SSH (fork mantenido de com.jcraft:jsch, abandonado desde 2018)
commons-net:commons-net3.13.0Cliente FTP (Apache Commons Net)
commons-io:commons-io2.22.0Utilidades de I/O (lectura de ficheros, Base64)
com.ibm.icu:icu4j78.3Transliteración Unicode a ASCII
com.fasterxml.jackson.core:jackson-databindgestionado por parentSerialización/deserialización JSON
com.googlecode.libphonenumber:libphonenumber9.0.30Validación y formateo de números de teléfono
com.google.guava:guava33.6.0-jreColecciones y utilidades Google
org.json:json20251224Parseo JSON básico
org.glassfish.jaxb:jaxb-runtimegestionado por parentJAXB para marshaling/unmarshaling XML
org.projectlombok:lombok1.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 propio pom.xml. Además, Lombok debe aparecer en <annotationProcessorPaths> del maven-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 externoProtocolo / ClienteDirección del flujoClase responsable
Servidor FTP genéricoFTP (Commons Net, modo binario pasivo)Saliente (upload)FtpUtils
Servidor SFTP genéricoSFTP/SSH (JSch fork mwiede)Bidireccional (upload/download)SftpUtils, SftpService
Google Cloud StorageHTTP/REST (SDK oficial)BidireccionalStorageUtils
Google Sheets API v4HTTP/REST (Google API Java client)Saliente (lectura/escritura por el consumidor)SheetsServiceUtils
Servidor de correo SMTPSMTP (Spring Mail / Jakarta Mail)Saliente (envío)MailUtils
URL HTTP/HTTPS externasHTTP (java.net.http.HttpClient)Entrante (descarga de ficheros)SeveralUtils.downloadFile
Google Artifact RegistryHTTPS / Maven WagonSaliente (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:

ClaveDescripciónEjemplo de valor
sftp.hostHostname o IP del servidor SFTPsftp.ejemplo.com
sftp.portPuerto del servidor SFTP22
sftp.userUsuario para autenticación SFTPusuario_sftp
sftp.passwordContraseña SFTP (sensible)********
sftp.remoteDirDirectorio 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 entornoDescripción
GOOGLE_APPLICATION_CREDENTIALSRuta 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.xml con 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:

EtapaDescripción
CheckoutObtiene el código fuente del repositorio (checkout scm)
Publish to Artifact RegistryEjecuta 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 Jenkinsfile actual, 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, StorageUtils y SftpService capturan internamente las excepciones y las registran con el logger. Los errores no se propagan al llamador en estos casos.
  • SftpUtils.connect y connectSession lanzan IllegalAccessException si 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, WARNING y SEVERE.
  • FtpUtils registra todos los comandos FTP mediante PrintCommandListener + LoggerOutputStream a nivel INFO.
  • SftpUtils registra 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.