🧱 Librería Commons Utils v 2.5.2
Esta guía resume cómo instalar, escanear, qué incluye y cómo usar la librería común. Pensada para que cualquier dev la consuma en minutos. ✨
📥 Instalación
Maven
<dependency>
<groupId>com.alpura</groupId>
<artifactId>back-java-lib-common-utilities</artifactId>
<version>2.5.2</version>
</dependency>
🔍 Escaneo de componentes
Agrega el paquete raíz para registrar AOP, validaciones y mappers:
@SpringBootApplication
@ComponentScan({
"com.alpura.utilities", // 🧠 Obligatorio
// tus paquetes…
})
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
🧱 ¿Qué incluye?
- VOs:
RequestVO,SingleResponseVO,ResponseVO,ResponseErrorVO,HeaderVO,NotificationVO,RecipientVO,FileVO,EmailVO,MultipartRequestVO,DescargaArchivoResponseVO,DownloadImagenRequestVO,ImagenEmailVO,ImagenBase64EmailVO,FileBase64EmailVO,ExcelSheetConfigVO,GenerateDataTemplateRequestVO,GeneraDataTemplateResponseVO,TrabajosRequestVO,TrabajosResponseVO,BaseVO,IdiomaVO,UserLoggedInfoVO. - Web:
BaseController(healthcheck, handler de validación y helpergetRequest()),HomeController(vista/conModelAndView). - Validación:
BaseNoBlankGroup(marker),ValidationConfiguration(MessageSource + ValidatorFactory). - Utils:
LocalDateUtil,ListUtil,StringUtil,ValidatorUtil,ValidationUtil,JsonConverterUtil(JPA),ResourceUtil,AuditoryUtils,BusinessErrorUtil. - Excepciones:
AlpuraException,ValidationException. - Errores:
GeneralBusinessErrors,BusinessError. - Mapper:
DozerConfig,LocalDateTimeToStringMapper,LocalDateToStringMapper. - Enums:
ChannelEnum,EmailDispositionEnum,ErrorCode. - Constantes:
GeneralConstants(formatos, regex, tokens, etc.). - AOP:
@JsonResponseInterceptor,JsonResponseHandlerInterceptor(normaliza errores a JSON estándar).
✨ Nuevas características
Manejo de Fechas Simplificado
- Ahora puedes declarar fechas como
Stringen tus VO yLocalDate/LocalDateTimeen tus DO. - Dozer hará la conversión automática con nuestros custom converters.
LocalDateTime⇄String:yyyy-MM-dd'T'HH:mmLocalDate⇄String:yyyy-MM-dd
Swagger/OpenAPI automático
- Los microservicios exponen documentación en
http://localhost:9090(verspringdocmás abajo).
🛠️ Mejoras
application.yml estandarizado con:
- Endpoints de gestión (
health,info,prometheus) - Seguridad Alpura
- Formatos de fecha y hora
🚨 Cambios Importantes (Breaking Changes)
- Actualiza
pom.xml→<properties>
<alpura.common.utilities.version>2.5.2</alpura.common.utilities.version>
src/main/resources/mappings/mappings.xml(crear si no existe)
<mappings xmlns="http://dozermapper.github.io/schema/bean-mapping">
<configuration>
<custom-converters>
<converter type="com.alpura.utilities.mapper.LocalDateTimeToStringMapper">
<class-a>java.time.LocalDateTime</class-a>
<class-b>java.lang.String</class-b>
</converter>
<converter type="com.alpura.utilities.mapper.LocalDateToStringMapper">
<class-a>java.time.LocalDate</class-a>
<class-b>java.lang.String</class-b>
</converter>
</custom-converters>
</configuration>
</mappings>
application.ymlestandarizado
spring:
profiles:
active:
- '@spring.profiles.active@'
dozer:
mapping-files:
- classpath:mappings/mappings.xml
server:
port: 9090
servlet:
context-path: /
springdoc:
api-docs:
path: /v3/api-docs
swagger-ui:
path: /swagger-ui.html
info:
title: Mi API de Alpura
version: v1.0.0
description: Documentación de la API de oficina de Alpura
terms-of-service: https://www.alpura.com/terminos
contact:
name: Equipo de Desarrollo de Alpura
email: desarrollo@alpura.com
license:
name: Apache 2.0
url: https://springdoc.org/license.html
audit:
config:
dbAuditable: true
logAuditable: true
alpuraSecurity:
evaluateRoles: false
disableSecurity: false
date:
string:
datetime:
format: "yyyy-MM-dd'T'HH:mm"
date:
format: yyyy-MM-dd
management:
endpoints:
web:
exposure:
include: health,info,prometheus
- @Transactional en Fachada
Agrega@Transactionalen métodos de la capa Facade para consistencia transaccional.
👩💻 Ejemplo práctico (VO ⇄ DO con fechas)
Objetivo: El dev trabaja con String en el VO y con tipos de fecha nativos en el DO. Dozer convierte automáticamente según los formatos configurados.
1) Value Object (VO) — expuesto en API
// Expone fechas como String (fáciles para front)
public class OrderVO {
private String orderId;
private String orderDate; // yyyy-MM-dd
private String deliveredAt; // yyyy-MM-dd'T'HH:mm
// getters/setters
}
2) Data Object (DO) — persistencia
import java.time.LocalDate;
import java.time.LocalDateTime;
@Entity
@Table(name = "orders")
public class OrderDO {
@Id
private Long id;
private LocalDate orderDate; // Solo fecha
private LocalDateTime deliveredAt; // Fecha y hora
// getters/setters
}
3) Mapeo (Dozer) — ¡sin reglas adicionales!
- Los converters globales (
LocalDateTimeToStringMapper,LocalDateToStringMapper) ya están activos pormappings.xml. - No necesitas crear
bean-mappingpor clase si los nombres coinciden.
4) Uso en servicio
import com.github.dozermapper.core.Mapper;
import org.springframework.beans.factory.annotation.Autowired;
@Service
public class OrderService {
@Autowired
private Mapper mapper;
@Autowired
private OrderRepository repo;
public OrderVO findAsVO(Long id) {
var entity = repo.findById(id).orElseThrow();
// DO -> VO (LocalDate/LocalDateTime → String usando formatos configurados)
return mapper.map(entity, OrderVO.class);
}
public void createFromVO(OrderVO vo) {
// VO -> DO (String → LocalDate/LocalDateTime)
var entity = mapper.map(vo, OrderDO.class);
repo.save(entity);
}
}
5) Prueba rápida (round-trip)
Entrada JSON (POST /orders):
{
"orderId": "A-1001",
"orderDate": "2025-08-01",
"deliveredAt": "2025-08-01T14:30"
}
En BD (persistido por JPA):
order_date:2025-08-01(LocalDate)delivered_at:2025-08-01 14:30:00(LocalDateTime)
Salida JSON (GET /orders/A-1001):
{
"orderId": "A-1001",
"orderDate": "2025-08-01",
"deliveredAt": "2025-08-01T14:30"
}
✅ Sin código manual de parseo/formateo — Dozer y los converters hacen el trabajo.
⚙️ Overriding de formatos por ambiente
Puedes cambiar los formatos sin recompilar, ajustando application.yml:
date:
string:
datetime:
format: "yyyy-MM-dd'T'HH:mm" # cambia aquí si lo necesitas
date:
format: yyyy-MM-dd
Los converters leen estos valores vía
@Value. Mantén elDozerConfigymappings.xmlactivos.
✅ Checklist de adopción rápida
- Subiste a
alpura-common-utilities:2.5. - Incluiste
mappings/mappings.xmlcon los converters. - Tienes
application.ymlestándar condate.string.(date|datetime).format. - Anotaste métodos de Fachada con
@Transactional. - Validaste VO/DO con un round-trip simple.
📦 VOs núcleo (Alpura)
Esta sección define los contratos obligatorios para entrada/salida en servicios y fachadas.
✅ Contratos estándar obligatorios en la arquitectura Alpura
- Entrada: RequestVO<T>
- Salida unitaria: SingleResponseVO<T>
- Salida paginada: ResponseVO<T>
📥 RequestVO<T> (entrada)
Estructura con contexto y payload de negocio en parameters.
public class RequestVO<T> {
Integer size; // Tamaño de página
Integer offset; // Desplazamiento (0-based)
String token; // Token (si aplica)
String macAddress;
T parameters; // 🧩 Payload de negocio
Date requestDate;
String orderBy, orderType; // Ordenamiento
Double latitude, longitude;
String idioma, timeZoneUsuario;
String idClienteInvoke, codigoOperacionCliente;
String appSource, deviceType, httpMethod;
}
🎯 SingleResponseVO<T> (salida unitaria)
public class SingleResponseVO<T> {
HeaderVO header = new HeaderVO(); // code/message
boolean success; // true/false
List<ResponseErrorVO> errors; // errores (si aplica)
T data; // resultado
String newToken; // opcional
}
📊 ResponseVO<T> (listado/paginación)
Extiende a SingleResponseVO<T> y agrega metadatos.
public class ResponseVO<T> extends SingleResponseVO<T> {
Long totalRows; // Total de registros
Integer page; // Índice de página (0-based)
}
🧾 HeaderVO
Encabezado ligero para toda respuesta.
public class HeaderVO {
int code = 0; // 0 = OK (convención)
String message = "OK"; // Mensaje breve
String detail; // Detalle adicional
}
🚨 ResponseErrorVO
Estructura de error uniforme.
public class ResponseErrorVO {
String code; // Código de error
String key; // Clave
String message; // Mensaje
String detail; // Detalle (opcional)
HttpStatus httpStatus;
}
✅ Validación — Grupos y Mensajes
Esta sección explica cómo activar y usar validaciones por grupos en tus VO (Value Objects) y cómo retornar errores uniformes desde tus controladores con la librería común de Alpura.
📌 Objetivo: marcar campos obligatorios sólo en escenarios concretos (por ejemplo, en
create/update) usando grupos de validación.
🏷️ Definir el grupo de validación
package com.alpura.utilities.validation;
public interface BaseNoBlankGroup {}
🧠 Este grupo te permite marcar anotaciones como obligatorias sólo cuando invocas
@Validated(BaseNoBlankGroup.class).
🧱 Anotar tus VO con grupos
A continuación un VO de ejemplo (puedes adaptarlo a tu ConfigurationVO o similar):
package com.example.demo.vo;
import com.alpura.utilities.validation.BaseNoBlankGroup;
import jakarta.validation.constraints.NotBlank;
import lombok.Getter;
import lombok.Setter;
@Getter @Setter
public class ConfigurationMinimalVO {
@NotBlank(message = "El nombre de la aplicación es obligatorio.", groups = BaseNoBlankGroup.class)
private String nameApplication;
@NotBlank(message = "El perfil es obligatorio.", groups = BaseNoBlankGroup.class)
private String profile;
@NotBlank(message = "Ejemplo", groups = BaseNoBlankGroup.class)
private String label;
@NotBlank(message = "Ejemplo", groups = BaseNoBlankGroup.class)
private String propKey;
@NotBlank(message = "Ejemplo", groups = BaseNoBlankGroup.class)
private String propValue;
}
🚦 Validar en el controlador
Usa @Validated(BaseNoBlankGroup.class) para activar las reglas sólo en ese endpoint.
Además, te recomendamos anotar con @JsonResponseInterceptor para respuestas de error uniformes.
@RestController
@RequestMapping("/api/v1/config")
public class ConfigurationController extends BaseController {
@PostMapping(path="/create", consumes=MediaType.APPLICATION_JSON_VALUE, produces=MediaType.APPLICATION_JSON_VALUE)
@JsonResponseInterceptor
public ResponseEntity<SingleResponseVO<Boolean>> create(
@Validated(BaseNoBlankGroup.class) @RequestBody ConfigurationMinimalVO body) {
}
}
📦 Estructura de respuesta de error (uniforme)
Cuando falla la validación, gracias al handler en BaseController y/o al AOP de @JsonResponseInterceptor, obtendrás una salida consistente:
{
"error": [
"El nombre de la aplicación es obligatorio.",
"El perfil es obligatorio."
],
"timeStamp": "2025-07-30T15:04:05.000+00:00",
"status": 400
}
En endpoints anotados con
@JsonResponseInterceptor, los errores también pueden envolver unSingleResponseVOconHeaderVOyerrorsdetallados.
🧰 Consejos
- 🧪 Aplica
@Validated(BaseNoBlankGroup.class)sólo donde quieras reglas estrictas (por ejemplo,POST /create,PUT /update). - 🧱 Deja reglas no críticas sin grupo para validarlas siempre que uses
@Validatedo@Validsin grupo. - 🛡️ Mantén
@JsonResponseInterceptoren controladores para respuestas homogéneas de errores (validación, negocio, genéricos).
🧭 Checklist rápido
-
BaseNoBlankGroupdisponible. -
ValidationConfigurationregistrado. - VO anotados con
groups = BaseNoBlankGroup.class. - Controladores con
@Validated(BaseNoBlankGroup.class)(+@JsonResponseInterceptor).
🚨 Errores, Excepciones y AOP de Respuesta Uniforme
Esta sección explica cómo declarar, lanzar y transportar errores en Alpura, y cómo estandarizar las respuestas JSON con AOP.
🧩 Piezas principales
- Excepciones
AlpuraExceptionValidationException
- Errores
BusinessErrorGeneralBusinessErrors
- Enums
ErrorCode
- AOP (respuesta JSON uniforme)
JsonResponseInterceptor(anotación)JsonResponseHandlerInterceptor(aspecto)
🧠 Objetivo: que todos los controladores retornen un formato homogéneo ante errores (validación, negocio o excepciones genéricas).
🧱 Excepciones
AlpuraException (negocio)
- Úsala para reglas de negocio, colisiones (conflict), no encontrado (not found), etc.
- Acepta un
ErrorCode,message(detalle) y opcionalmenteHttpStatus.
throw new AlpuraException(
ErrorCode.REQUIRED_FIELD,
"La configuración ya existe",
HttpStatus.CONFLICT
);
También puedes construirla sólo con ErrorCode o con int code:
throw new AlpuraException(ErrorCode.NO_RESULTS); // sin HttpStatus → 500 por defecto (lo resuelve el AOP)
throw new AlpuraException(1001, "Formato inválido"); // entero custom
ValidationException (validación manual)
- Úsala cuando quieras forzar una falla de validación fuera de Bean Validation.
if (algoInvalido) {
throw new ValidationException(ErrorCode.REQUIRED_FIELD, "Campo requerido faltante");
}
📝 Para errores de Bean Validation (anotaciones
@NotBlank, etc.) el handler deBaseControllerya devuelve 400 con el detalle.
🔢 Enum ErrorCode
Algunos códigos incluidos:
public enum ErrorCode {
UNKNOWN_ERROR(0),
REQUIRED_FIELD(1),
EXCEEDS_MAX_LENGTH(2),
INVALID_DATE_FORMAT(1001),
NO_RESULTS(404);
// getCode()
}
Buenas prácticas
- Usa
ErrorCodepara comunicar la causa técnica/funcional. - El mensaje legible lo controla el AOP y
message.properties/envcuando sea necesario.
🛡️ Respuesta JSON uniforme con AOP
Anotación: @JsonResponseInterceptor
Colócala en métodos del controlador que deban estandarizar su salida de error:
@RestController
@RequestMapping("/api/v1/demo")
public class DemoController {
@GetMapping("/boom")
@JsonResponseInterceptor
public ResponseEntity<SingleResponseVO<Boolean>> boom() {
// Lanza algo… será capturado y normalizado:
throw new AlpuraException(ErrorCode.NO_RESULTS, "No hay datos", HttpStatus.NOT_FOUND);
}
}
Aspecto: JsonResponseHandlerInterceptor
- Intercepta el método, captura:
ValidationException→ 400AlpuraException→ usa elHttpStatusinterno o 500Exception→ 500
- Construye una respuesta estándar con
SingleResponseVO,HeaderVOy lista deResponseErrorVO.
Ejemplo de salida (404):
{
"header": { "code": 404, "message": "Not Found" },
"success": false,
"errors": [
{
"code": "404",
"key": null,
"message": "…mensaje resuelto…",
"detail": "No hay datos",
"httpStatus": "NOT_FOUND"
}
],
"data": null
}
🔎 El
messagedeResponseErrorVOse puede resolver conenv.getProperty("arquetipo.error.<code>"). Define esos textos si deseas mensajes por código.
🧪 Ejemplos rápidos
1) Lanzar AlpuraException en Service
if (repo.existsByClave(clave)) {
throw new AlpuraException(ErrorCode.REQUIRED_FIELD, "La clave ya está en uso", HttpStatus.CONFLICT);
}
2) Controlador con AOP
@PostMapping("/create")
@JsonResponseInterceptor
public ResponseEntity<SingleResponseVO<Boolean>> create(@RequestBody Foo body) {
// Si el service lanza AlpuraException, el AOP devuelve un JSON uniforme
return ResponseEntity.ok(new SingleResponseVO<>(true));
}
3) Error de validación (Bean Validation)
Si usas @Validated(BaseNoBlankGroup.class) y fallan campos:
- El
@ExceptionHandlerenBaseControllerretorna 400 con lista de mensajes:
{
"error": ["campo X es obligatorio", "campo Y inválido"],
"timeStamp": "2025-07-30T10:00:00Z",
"status": 400
}
✅ Checklist de adopción
- Lanzar errores de negocio con
AlpuraException(usaErrorCode+HttpStatusdonde aplique). - Anotar endpoints con
@JsonResponseInterceptorpara respuestas homogéneas. - (Opcional) Definir
arquetipo.error.<code>enapplication.ymlomessage.properties. - Mantener
.propertiesde errores por idioma (si usasBusinessErrorUtil). - Evitar filtrar stacktrace a producción (
arquetipo.allowTrace: false).
💡 Consejos
- Usa
ErrorCodepara consistencia entre servicios y trazabilidad. - Mantén el AOP en todos los controladores públicos para evitar respuestas inconsistentes.
- Para validación por grupo, combina con
@Validated(BaseNoBlankGroup.class)en endpoints de alta/modificación.