Una de las sorpresas que uno se lleva cuando migra a una versión superior de tomcat por ejemplo 10.x o tomcat 11.x , que claro es evidente que se requieren ajustes eso no es nada nuevo, lo que si es nuevo es que Jersey 3.x ya cambio nombre de espacio de javax a jakarta, eso no es tan significativo lo que si lo es , es que Jersey 2.x , en general necesitaba algunas pocas librerías , pero en Jersey 3.x , por ser modular para "facilitar" la vida del programador , necesita una cantidad interesante de librerías jar para funcionar, la solución que dan los expertos, es lo mas normal del mundo usar Maven , para que instale el repositorio de librería adecuado, y sugieren este pom.xml para maven.
Jersey 3.1.3 ya no es compatible directamente con Java EE/Jakarta EE clásicos, porque todo fue migrado al espacio de nombres jakarta.*
(en lugar de javax.*
). Este cambio obligó a Jersey a modificar su arquitectura interna, y eso cambió las dependencias necesarias para que funciones como @Consumes(MediaType.APPLICATION_JSON)
trabajen correctamente.
<dependencies>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
<version>3.1.3</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>3.1.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-jakarta-xmlbind-annotations</artifactId>
<version>2.17.0</version>
</dependency>
</dependencies>
Nada especial caso del día de un programador, más si esta migrando, hasta hay bien, lo que si es del otro mundo, es que no es cierto que instala todas las dependencias que se necesita, y cuando inicia tu flamante tomcat 11 con 17 y jersey 3.x no encuentras ningún error ni de compilaciones en el registro de /log/catalina.out , y levanta el stack jersey, entonces supones que todo es bien, pero eso no es cierto, cuando intentas ejecutar tu aplicación empinan a aparecer errores del tipo 309, 403, 415, 500 y un largo etc, que piensas ?, lo lógico que tu programa tiene problemas, y empezar actualizar , revisar, mejorar , pero después de frustrarte y terminar sin ver ningún error que te ayude, puede que estés de buenas y aparezca un error 500, o 415 , y eso ya es un indicio que tu jersey 3.x tiene problemas, y problemas serios de dependencias, luego de batallar puedes llegar que todas las dependencias necesarias son las siguientes por lo menos en mi caso fueron esas y al final funciono:
/webapps/miApi/lib
-rw-r--r--. 1 tomcat tomcat 27415 May 7 14:37 aopalliance-repackaged-3.0.4.jar
-rw-r--r--. 1 tomcat tomcat 4210756 May 7 14:37 byte-buddy-1.14.9.jar
-rw-r--r--. 1 tomcat tomcat 67815 May 7 14:37 classmate-1.5.1.jar
-rw-r--r--. 1 tomcat tomcat 1328694 May 7 14:37 hibernate-validator-7.0.5.Final.jar
-rw-r--r--. 1 tomcat tomcat 207249 May 7 14:37 hk2-api-3.0.4.jar
-rw-r--r--. 1 tomcat tomcat 205291 May 7 14:37 hk2-locator-3.0.4.jar
-rw-r--r--. 1 tomcat tomcat 132025 May 7 14:37 hk2-utils-3.0.4.jar
-rw-r--r--. 1 tomcat tomcat 26147 May 7 14:37 istack-commons-runtime-4.1.1.jar
-rw-r--r--. 1 tomcat tomcat 78488 May 7 14:37 jackson-annotations-2.17.0.jar
-rw-r--r--. 1 tomcat tomcat 581556 May 7 14:37 jackson-core-2.17.0.jar
-rw-r--r--. 1 tomcat tomcat 1649184 May 7 14:37 jackson-databind-2.17.0.jar
-rw-r--r--. 1 tomcat tomcat 32407 May 7 14:37 jackson-module-jakarta-xmlbind-annotations-2.17.0.jar
-rw-r--r--. 1 tomcat tomcat 66651 May 7 14:37 jakarta.activation-api-2.1.1.jar
-rw-r--r--. 1 tomcat tomcat 26141 May 7 14:37 jakarta.annotation-api-2.1.1.jar
-rw-r--r--. 1 tomcat tomcat 173677 May 7 14:37 jakarta.el-4.0.2.jar
-rw-r--r--. 1 tomcat tomcat 80563 May 7 14:37 jakarta.el-api-4.0.0.jar
-rw-r--r--. 1 tomcat tomcat 10681 May 7 14:37 jakarta.inject-api-2.0.1.jar
-rw-r--r--. 1 tomcat tomcat 92678 May 7 14:37 jakarta.validation-api-3.0.0.jar
-rw-r--r--. 1 tomcat tomcat 154815 May 7 14:37 jakarta.ws.rs-api-3.1.0.jar
-rw-r--r--. 1 tomcat tomcat 127111 May 7 14:37 jakarta.xml.bind-api-4.0.0.jar
-rw-r--r--. 1 tomcat tomcat 794137 May 7 14:37 javassist-3.29.2-GA.jar
-rw-r--r--. 1 tomcat tomcat 138671 May 7 14:37 jaxb-core-4.0.3.jar
-rw-r--r--. 1 tomcat tomcat 920315 May 7 14:37 jaxb-runtime-4.0.3.jar
-rw-r--r--. 1 tomcat tomcat 60790 May 7 14:37 jboss-logging-3.4.1.Final.jar
-rw-r--r--. 1 tomcat tomcat 305942 May 7 14:37 jersey-client-3.1.3.jar
-rw-r--r--. 1 tomcat tomcat 1213519 May 7 14:37 jersey-common-3.1.3.jar
-rw-r--r--. 1 tomcat tomcat 32920 May 7 14:37 jersey-container-servlet-3.1.3.jar
-rw-r--r--. 1 tomcat tomcat 74544 May 7 14:37 jersey-container-servlet-core-3.1.3.jar
-rw-r--r--. 1 tomcat tomcat 84885 May 7 14:37 jersey-entity-filtering-3.1.3.jar
-rw-r--r--. 1 tomcat tomcat 78428 May 7 14:37 jersey-hk2-3.1.3.jar
-rw-r--r--. 1 tomcat tomcat 80721 May 7 14:37 jersey-media-json-jackson-3.1.3.jar
-rw-r--r--. 1 tomcat tomcat 985504 May 7 14:37 jersey-server-3.1.3.jar
-rw-r--r--. 1 tomcat tomcat 19479 May 7 14:37 osgi-resource-locator-1.0.3.jar
-rw-r--r--. 1 tomcat tomcat 817 May 7 14:37 test-resource.jar
-rw-r--r--. 1 tomcat tomcat 73269 May 7 14:37 txw2-4.0.3.jar
Casi la tercera parte me tocó agregarlas mediante el comando wget), a medida que iba encontrando problemas, esto después de haber importando "todas" las dependencias "necesarias" con Maven supuestamente.
También hay que tener en cuenta que debes registrar algunas dependencias en tu backend , eso no es nada nuevo pero hay que tenerlo en cuenta
en mi caso fueron.
miApplication.java
=====================================================
package miPaquete;
import jakarta.ws.rs.ApplicationPath;
import jakarta.ws.rs.core.Application;
import java.util.HashSet;
import java.util.Set;
import org.glassfish.jersey.jackson.JacksonFeature;
@ApplicationPath("api")
public class ZephyrApplication extends Application {
@Override
public Set<Class<?>> getClasses() {
Set<Class<?>> classes = new HashSet<>();
// Registrar recursos
classes.add(ZephyrResource.class); // Registra tu recurso principal
// Registrar filtros
classes.add(CORSFilter.class); // Registra el filtro CORS
// Proveedor JSON
classes.add(JacksonFeature.class);
// Puedes agregar más filtros o proveedores si es necesario
// classes.add(MiFiltro.class); // Filtro personalizado
// classes.add(MiProveedor.class); // Proveedor personalizado
return classes;
}
}
y por su puesto el CORSFilter.java , yo lo hice asi, pero se pudo haber hecho en el archivo web.xml , sin problema, yo paso una variable de interes para mi. USUARIO_DB. que me permite algunos manejos de mi database.
CORSFilter.kava
===========================================================
package byb.zephyr;
import jakarta.ws.rs.container.ContainerRequestContext;
import jakarta.ws.rs.container.ContainerResponseContext;
import jakarta.ws.rs.container.ContainerResponseFilter;
import jakarta.ws.rs.ext.Provider;
import java.io.IOException;
@Provider
public class CORSFilter implements ContainerResponseFilter {
@Override
public void filter(ContainerRequestContext requestContext,
ContainerResponseContext responseContext) throws IOException {
responseContext.getHeaders().putSingle("Access-Control-Allow-Origin", "*");
responseContext.getHeaders().putSingle("Access-Control-Allow-Headers",
"origin, content-type, accept, authorization, X-API-KEY, X-USUARIO-DB");
responseContext.getHeaders().putSingle("Access-Control-Allow-Methods",
"GET, POST, PUT, DELETE, OPTIONS, HEAD");
responseContext.getHeaders().putSingle("Access-Control-Max-Age", "1728000");
}
}
y luego claro el API como tal en este caso tengo una entrada para extrae mis agentes comerciales:
miResource.java
==================================================
package mipackage;
import org.slf4j.Logger ;
import org.slf4j.LoggerFactory ;
import java.util.* ;
import jakarta.ws.rs.Path ;
import jakarta.ws.rs.Produces ;
import jakarta.ws.rs.core.MediaType ;
import jakarta.ws.rs.* ;
import jakarta.ws.rs.core.Context ;
import jakarta.ws.rs.core.HttpHeaders;
import jakarta.ws.rs.core.Response ;
import com.mongodb.client.* ;
import com.mongodb.client.* ;
@Path("/")
public class miResource {
private static final Logger logger = LoggerFactory.getLogger(ZephyrResource.class);
private boolean ValidarApiKey(HttpHeaders headers) {
String apiKey = headers.getHeaderString("X-API-KEY") ;
return "miKey".equals(apiKey); // Reemplazar "your-api-key" con tu API key real
}
@POST
@Path("/AgentesConsultarID")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response AgentesID(@Context HttpHeaders headers, AgentesDTO agente) {
logger.info("acceso");
if (! ValidarApiKey(headers)) {
logger.error("API key inválida. Acceso denegado.");
return Response.status(Response.Status.UNAUTHORIZED).entity("Invalida API key").build();
}
logger.info("Conectando a la base de datos...");
ConectarDB(headers) ;
logger.debug("Datos del agente recibido: {}", agente);
AgentesDAO dao = new AgentesDAO();
Vector<AgentesDTO> resultados = dao.AgentesID(mgDB,agente);
logger.info("Consultados {} resultados de agentes", resultados.size());
return Response.ok(resultados).build();
}
}
Recordar colocar un par de entradas al path de tu script de compilación
los siguientes jar qe debes tener en un lugar accesible por javac, yo las coloco en /lib por facilidad.
- jersey-media-json-jackson-3.1.3.jar
- jersey-server-3.1.3.jar
- jakarta.ws.rs-api-3.1.0.jar
y por ultimo generar el web.xml en la carpeta de tu api, webapps/ miApi/WEB-INF/web.xml
yo lo tengo minimalista porque deje todo el peso de la gestión en los archivos CORSFilter.java y
miApplication.java , buneo en eso si hay gustos. mi web.xml qudó.
web.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="https://jakarta.ee/xml/ns/jakartaee" xsi:schemaLocation="https://jaka\
rta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd" version="5.0">
<display-name>bybapi</display-name>
<servlet>
<servlet-name>Jersey Web Application</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jakarta.ws.rs.Application</param-name>
<param-value>miApi.mi.miApplication</param-value>
<!-- O byb.zephyr.ApplicationConfig, dependiendo de cuál quieras usar -->
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Jersey Web Application</servlet-name>
<url-pattern>/api/*</url-pattern>
</servlet-mapping>
</web-app>
Adicionalmente generé este script para ejecutar en node, solamente por facilidad y evitar meter
mas variables en la depuración e ir directamente a la fuente del problema en este sistema :
prueba.mjs para node
================
Tener en cuenta que se debe usar (-) y no (_) por si usa algún sistema como Nginx o Apache
estos campos en la cabecera son bloqueados , recomienda el prefijo X- , para que cumpla con
las recomendaciones W3C. Este script es una herramienta de prueba que ayuda a drpurar
problemas de las librerias faltantes y logica de la API, pero no es apta para depurar errores de
CORS. Porque node no es un navegador
import fetch from 'node-fetch'; // Asegúrate de tener instalada la librería 'node-fetch'
const apiUrl = 'https:miapi.misitio.com/api/AgentesConsultarID';
const headers = {
'Content-Type' : 'application/json',
'X-API-KEY' : 'miclave',
'X-USUARIO-DB' : 'usuario001',
'Authorization' : 'Bearer token'
};
const data = {
agenteLimit : "25",
agenteOffSet : "0",
agenteBuscar : "",
agenteSort : "NOMBRE",
agenteEntorno: "usuario001"
};
fetch(apiUrl, {
method: 'POST',
headers: headers,
body: JSON.stringify(data) //pasar datos JSON
})
.then(async res => {
const text = await res.text(); // Obtener la respuesta cruda
console.log('Respuesta cruda:\n', text); // Mostrar la respuesta
try {
const json = JSON.parse(text);
console.log('Respuesta JSON:', json);
} catch (err) {
console.error('⚠️ No se pudo parsear como JSON:', err.message);
}
})
.catch(err => console.error('Error de red:', err));
script NGIX para proxy inverso
========================
server {
listen 80;
server_name bybapi.burgosyburgossas.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name miSitiocom;
ssl_certificate /etc/letsencrypt/live/bybapi.burgosyburgossas.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/bybapi.burgosyburgossas.com/privkey.pem;
location / {
if ($request_method = OPTIONS) {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization, X-API-KEY, X-USUARIO-DB';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Length' 0;
add_header 'Content-Type' 'text/plain charset=UTF-8';
return 204;
}
proxy_pass http://localhost:8080/bybapi/;
proxy_set_header Host $host;
proxy_set_header X-USUARIO-DB $http_x_usuario_db;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Caso de uso:
Ejemplo.html
=========
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Ejemplo de consulta API</title>
</head>
<body>
<h2>Consultar Agentes</h2>
<button onclick="consultarAgentes()">Consultar</button>
<h3>Respuesta del servidor:</h3>
<pre id="resultado" style="background: #eee; padding: 1em;"></pre>
<script>
function consultarAgentes() {
const datos = {
agenteLimit : "25",
agenteOffSet : "0",
agenteBuscar : "",
agenteSort : "NOMBRE",
agenteEntorno : "mi001"
};
const headers = {
"X-API-KEY" : "miKey",
"X-USUARIO-DB" : "mi001",
"Authorization" : "Bearer token",
"Accept" : "application/json",
"Content-Type" : "application/json"
};
fetch("https://miSitio.com/api/AgentesConsultarID", {
method : 'POST',
headers: headers,
body: JSON.stringify(datos)
})
.then(response => {
if (!response.ok) {
throw new Error("Error en la respuesta: " + response.status);
}
return response.json();
})
.then(data => {
console.log("Respuesta:", data);
document.getElementById("resultado").innerText = JSON.stringify(data, null, 2);
})
.catch(error => {
console.error("Error:", error);
document.getElementById("resultado error").innerText = "Error: " + error.message;
});
}
</script>
</body>
</html>
Al final después de hacer una análisis al proceso es que todo se resumen a que los importadores de jar en en este caso en MAVEN y los pom están incompletos, pero para llegar a eso toco pasar por proxy inverso, Nginx. certificados de seguridad LetsEncript, el navegador, permisos, logger, la lógica de los programas, tomcat, java , como no se ven errores, pues se sospecha de todo porque no funciona.
Comentarios
Publicar un comentario