- VERSION 5.09.16

- feat: Implementa tolerancia de parámetros configurable y mejora estabilidad general del servidor.
- La tolerancia de parametros permite que si un query requiere 3 parametros y se mandan 4, NO mande un error,
	solo manda a la base de datos los parametros correctos y tira los extras, y guarda una "ADVERTENCIA" en el Log de errores.

- Este commit introduce la funcionalidad de `parameterTolerance` configurable y aborda varias mejoras críticas para la estabilidad y eficiencia del jRDC2-Multi.

- Principales cambios y beneficios:
- **Tolerancia de Parámetros**: Añade la propiedad `parameterTolerance` en `config.properties` para controlar el manejo de parámetros de más. Cuando está habilitada, recorta los parámetros excesivos; si está deshabilitada (modo estricto, por defecto), genera un error, aumentando la robustez de la validación.
- **Inicialización Multi-DB Confiable**: Corrige la lógica de inicialización en `Main.AppStart` para `RDCConnector` de DB3 y DB4, asegurando que cada base de datos tenga su propio *pool* de conexiones correctamente configurado.
- **Optimización de Ejecución SQL**: Elimina llamadas duplicadas a `ExecQuery2` y `ExecNonQuery2` en `DBHandlerB4X.bas`, garantizando que solo los parámetros validados se utilicen y evitando ejecuciones redundantes en la base de datos.
- **Refactorización y Limpieza**: Se eliminó la declaración duplicada de `ActiveRequestsCountByDB` en `Main.bas` y la subrutina `Handle0` obsoleta en `Manager.bas`, mejorando la claridad y mantenibilidad del código.
This commit is contained in:
2025-09-18 22:30:22 -06:00
parent 51c829b876
commit 3b352bb105
14 changed files with 537 additions and 466 deletions

View File

@@ -16,52 +16,32 @@ Sub Process_Globals
' - Agregar que el "Test" del manager revise (con el query de Jorge) cuantas conexiones hay actualmente activas, ' - Agregar que el "Test" del manager revise (con el query de Jorge) cuantas conexiones hay actualmente activas,
' - o si no en el test, un nuevo handler, talvez "Conexiones". ' - o si no en el test, un nuevo handler, talvez "Conexiones".
' - Agregar una forma de probar con carga el servidor ' - Agregar una forma de probar con carga el servidor
'- Agregar la opcion de "Queries lentos" '
' - VERSION 5.09.16
' feat: Implementa tolerancia de parámetros configurable y mejora estabilidad general del servidor.
' La tolerancia de parametros permite que si un query requiere 3 parametros y se mandan 4, NO mande un error,
' solo manda a la base de datos los parametros correctos y tira los extras, y guarda una "ADVERTENCIA" en el Log de errores.
'
' Este commit introduce la funcionalidad de `parameterTolerance` configurable y aborda varias mejoras críticas para la estabilidad y eficiencia del jRDC2-Multi.
'
' Principales cambios y beneficios:
' - **Tolerancia de Parámetros**: Añade la propiedad `parameterTolerance` en `config.properties` para controlar el manejo de parámetros de más. Cuando está habilitada, recorta los parámetros excesivos; si está deshabilitada (modo estricto, por defecto), genera un error, aumentando la robustez de la validación.
' - **Inicialización Multi-DB Confiable**: Corrige la lógica de inicialización en `Main.AppStart` para `RDCConnector` de DB3 y DB4, asegurando que cada base de datos tenga su propio *pool* de conexiones correctamente configurado.
' - **Optimización de Ejecución SQL**: Elimina llamadas duplicadas a `ExecQuery2` y `ExecNonQuery2` en `DBHandlerB4X.bas`, garantizando que solo los parámetros validados se utilicen y evitando ejecuciones redundantes en la base de datos.
' - **Refactorización y Limpieza**: Se eliminó la declaración duplicada de `ActiveRequestsCountByDB` en `Main.bas` y la subrutina `Handle0` obsoleta en `Manager.bas`, mejorando la claridad y mantenibilidad del código.
'
' - VERSION 5.09.15 ' - VERSION 5.09.15
' - feat: Consolidación de mejoras en monitoreo, gestión de pools y hot-swap de configuración.
' '
' Este commit integra y consolida todas las mejoras recientes en la robustez, ' 1. **Nuevas Funcionalidades en el Panel de Administración (Manager):**
' monitoreo de rendimiento y flexibilidad del servidor jRDC2-Multi.
'
' **Módulos Afectados:** Main.bas, RDCConnector.bas, DBHandlerB4X.bas, DBHandlerJSON.bas, Manager.bas, GlobalParameters.bas.
'
' **Cambios Clave Implementados:**
'
' 1. **Monitoreo Preciso y Robusto del Pool de Conexiones (C3P0) y Peticiones Activas:**
' * Se corrigieron las métricas de `BusyConnections` y `TotalConnections` en `query_logs` y el panel `Manager` para reflejar el estado real del pool de C3P0. Esto se logró capturando `BusyConnections` directamente del pool *inmediatamente después* de que un handler adquiere una conexión en `DBHandlerJSON.bas` y `DBHandlerB4X.bas` [1, 2].
' * El contador de peticiones activas por base de datos (`GlobalParameters.ActiveRequestsCountByDB`) ahora se incrementa y decrementa de forma consistente. Se asegura que la `dbKey` se resuelva *antes* de la operación de conteo y se aplica una coerción explícita a `Int` (`.As(Int)`) para todas las operaciones, resolviendo inconsistencias de tipo [3, 4].
' * La lógica de decremento en la subrutina `Private Sub CleanupAndLog` (presente en `DBHandlerJSON.bas` y `DBHandlerB4X.bas`) se hizo más robusta, verificando que el contador sea mayor que cero antes de decrementar para evitar valores negativos [5].
' * Beneficio: Monitoreo preciso y fiable en tiempo real del uso del pool de conexiones y la carga de peticiones activas, mejorando el diagnóstico y la estabilidad del servidor [5, 6].
'
' 2. **Implementación Completa de "Hot-Swap" para Recarga de Configuraciones de DB:**
' * La lógica del comando `reload` en `Manager.bas` fue completamente rediseñada para permitir la recarga dinámica de configuraciones de bases de datos sin reiniciar el servidor [7, 8].
' * Se utiliza una instancia de `java.util.concurrent.locks.ReentrantLock` (`MainConnectorsLock`) declarada e inicializada en `Main.bas` para proteger de forma atómica la lectura y reemplazo del mapa `Main.Connectors`, que es compartido por múltiples hilos [9, 10].
' * Se añadió un método `Public Sub Close` en `RDCConnector.bas` [8], que utiliza `JavaObject` para invocar el método `close()` del `ConnectionPool` (C3P0) subyacente. Esto permite un cierre ordenado de los pools de conexión antiguos, liberando sus recursos de la base de datos de manera limpia durante el "hot-swap" [11, 12].
' * La implementación en `Manager.bas` incluye un manejo seguro del bloqueo sin `Finally` (usando una bandera booleana `lockAcquired`) y lógica de validación para abortar la recarga si ocurren errores críticos, manteniendo los conectores antiguos activos para evitar interrupciones del servicio [12, 13].
' * Beneficio: Capacidad crítica para actualizar configuraciones de conexión a bases de datos en caliente, mejorando la disponibilidad, simplificando el mantenimiento y previniendo fugas de recursos [14].
'
' 3. **Manejo Mejorado de Peticiones POST con JSON en el Cuerpo:**
' * `DBHandlerJSON.bas` fue modificado para detectar y procesar correctamente las peticiones POST que envían el payload JSON directamente en el cuerpo (con `Content-Type: application/json`), en lugar de solo en el parámetro `j` de la URL [14, 15].
' * Se asegura la lectura completa del `InputStream` y su cierre explícito para liberar recursos [15, 16].
' * Beneficio: Compatibilidad con estándares API web modernos, mejorando la robustez y la adherencia a los estándares sin comprometer la retrocompatibilidad con el "Método Legacy" (GET con parámetro `j`) [16].
'
' 4. **Robustecimiento de la Inicialización del Pool de Conexiones (C3P0):**
' * La subrutina `Initialize` en `RDCConnector.bas` fue reordenada y fortalecida. Ahora asegura que la configuración de C3P0 se cargue completamente en la variable de clase `config` y que todas las propiedades del pool se apliquen mediante `jo.RunMethod` *inmediatamente después* de `pool.Initialize` y *antes* de que el pool intente adquirir conexiones [17, 18].
' * Se añadieron las líneas `jo.RunMethod("setAcquireRetryAttempts", Array As Object(1))` y `jo.RunMethod("setBreakAfterAcquireFailure", Array As Object(True))` en `RDCConnector.bas`. Estas son cruciales para forzar a C3P0 a lanzar una `SQLException` explícita si falla al crear las conexiones iniciales, en lugar de fallar silenciosamente [18, 19].
' * Se implementó una "activación forzada" del pool (`Dim tempCon As SQL = pool.GetConnection` seguido de `tempCon.Close`) dentro de un bloque `Try...Catch` en `RDCConnector.Initialize`. Esto obliga al pool a establecer las conexiones iniciales (`InitialPoolSize`) con la configuración ya aplicada, permitiendo la captura de errores reales si la conexión a la base de datos falla [20].
' * Beneficio: Diagnóstico temprano y preciso de problemas de conexión a la base de datos, evitando situaciones donde `TotalConnections` mostraba `0` o la `jdbcUrl` aparecía truncada [19, 21].
'
' 5. **Nuevas Funcionalidades en el Panel de Administración (Manager):**
' * Se añadió el comando `slowqueries` al `Manager` para permitir la visualización de las 20 consultas más lentas registradas en la tabla `query_logs` de SQLite [22]. ' * Se añadió el comando `slowqueries` al `Manager` para permitir la visualización de las 20 consultas más lentas registradas en la tabla `query_logs` de SQLite [22].
' * Se mejoró el comando `totalcon` en `Manager.bas` para mostrar estadísticas detalladas de *todos* los pools de conexión C3P0 configurados, obteniendo métricas en tiempo real (TotalConnections, BusyConnections, IdleConnections, etc.) de cada `RDCConnector` [2, 22]. ' * Se mejoró el comando `totalcon` en `Manager.bas` para mostrar estadísticas detalladas de *todos* los pools de conexión C3P0 configurados, obteniendo métricas en tiempo real (TotalConnections, BusyConnections, IdleConnections, etc.) de cada `RDCConnector` [2, 22].
' * Beneficio: Mayor visibilidad y control proactivo sobre el rendimiento y el uso de recursos del servidor desde la interfaz de administración. ' * Beneficio: Mayor visibilidad y control proactivo sobre el rendimiento y el uso de recursos del servidor desde la interfaz de administración.
' '
' 6. **Optimización de la Gestión de Logs (`query_logs`):** ' 2. **Optimización de la Gestión de Logs (`query_logs`):**
' * Se implementó un `Public timerLogs As Timer` en `Main.bas` [conversación], que se inicializa en `AppStart` y ejecuta periódicamente (cada 10 minutos) la subrutina `borraArribaDe15000Logs`. ' * Se implementó un `Public timerLogs As Timer` en `Main.bas` [conversación], que se inicializa en `AppStart` y ejecuta periódicamente (cada 10 minutos) la subrutina `borraArribaDe15000Logs`.
' * La subrutina `borraArribaDe15000Logs` recorta la tabla `query_logs` en `users.db` para mantener solo los 15,000 registros más recientes, y luego realiza un `vacuum` para optimizar el espacio en disco utilizado por la base de datos SQLite [conversación]. ' * La subrutina `borraArribaDe15000Logs` recorta la tabla `query_logs` en `users.db` para mantener solo los 15,000 registros más recientes, y luego realiza un `vacuum` para optimizar el espacio en disco utilizado por la base de datos SQLite [conversación].
' * Beneficio: Prevención del crecimiento excesivo de la base de datos de logs de rendimiento, manteniendo un historial manejable y optimizando el uso del almacenamiento a largo plazo. ' * Beneficio: Prevención del crecimiento excesivo de la base de datos de logs de rendimiento, manteniendo un historial manejable y optimizando el uso del almacenamiento a largo plazo.
'
' - VERSION 5.09.14 (Ahora consolidado en 5.09.15) ' - VERSION 5.09.14 (Ahora consolidado en 5.09.15)
' -feat: Implementación robusta de monitoreo de pool de conexiones y peticiones activas ' -feat: Implementación robusta de monitoreo de pool de conexiones y peticiones activas
' -Este commit resuelve problemas críticos en el monitoreo del pool de conexiones (C3P0) y el conteo de peticiones activas por base de datos, mejorando significativamente la visibilidad y fiabilidad del rendimiento del servidor jRDC2-Multi. ' -Este commit resuelve problemas críticos en el monitoreo del pool de conexiones (C3P0) y el conteo de peticiones activas por base de datos, mejorando significativamente la visibilidad y fiabilidad del rendimiento del servidor jRDC2-Multi.
@@ -81,7 +61,7 @@ Sub Process_Globals
' * **Monitoreo Preciso y Fiable**: Las métricas `busy_connections` y `handler_active_requests` en `query_logs` y el panel `Manager` ahora son totalmente fiables, proporcionando una visión clara y en tiempo real del uso del pool de conexiones y la carga de peticiones activas por base de datos. ' * **Monitoreo Preciso y Fiable**: Las métricas `busy_connections` y `handler_active_requests` en `query_logs` y el panel `Manager` ahora son totalmente fiables, proporcionando una visión clara y en tiempo real del uso del pool de conexiones y la carga de peticiones activas por base de datos.
' * **Diagnóstico Mejorado**: La visibilidad interna del estado del pool de C3P0 durante las pruebas confirma que la configuración de `RDCConnector` es correcta y que el pool se expande y contrae según lo esperado por la demanda. ' * **Diagnóstico Mejorado**: La visibilidad interna del estado del pool de C3P0 durante las pruebas confirma que la configuración de `RDCConnector` es correcta y que el pool se expande y contrae según lo esperado por la demanda.
' * **Robustez del Código**: La gestión de contadores de peticiones activas es ahora consistente, thread-safe y a prueba de fallos de tipo, mejorando la estabilidad general del servidor bajo carga. ' * **Robustez del Código**: La gestión de contadores de peticiones activas es ahora consistente, thread-safe y a prueba de fallos de tipo, mejorando la estabilidad general del servidor bajo carga.
'
' - VERSION 5.09.13.3 (Ahora consolidado en 5.09.15) ' - VERSION 5.09.13.3 (Ahora consolidado en 5.09.15)
' - Implementación de "Hot-Swap" para recarga de configuraciones de DB sin reiniciar el servidor. ' - Implementación de "Hot-Swap" para recarga de configuraciones de DB sin reiniciar el servidor.
' - Migración a ReentrantLock para sincronización debido a incompatibilidad con 'Sync'. ' - Migración a ReentrantLock para sincronización debido a incompatibilidad con 'Sync'.
@@ -106,7 +86,7 @@ Sub Process_Globals
' - * **Validación de inicialización y control de errores:** Se agregó lógica para verificar el éxito de la inicialización de los nuevos conectores y abortar el "hot-swap" si ocurre un error crítico, manteniendo los conectores antiguos activos para evitar una interrupción del servicio . ' - * **Validación de inicialización y control de errores:** Se agregó lógica para verificar el éxito de la inicialización de los nuevos conectores y abortar el "hot-swap" si ocurre un error crítico, manteniendo los conectores antiguos activos para evitar una interrupción del servicio .
' - * **Registro detallado:** Se mejoró la salida del log HTML del `Manager` para mostrar el proceso de recarga, las estadísticas de los pools recién inicializados y el cierre de los antiguos, incluyendo JSON detallado de las métricas de C3P0 . ' - * **Registro detallado:** Se mejoró la salida del log HTML del `Manager` para mostrar el proceso de recarga, las estadísticas de los pools recién inicializados y el cierre de los antiguos, incluyendo JSON detallado de las métricas de C3P0 .
' - • Beneficio: Estos cambios dotan al servidor jRDC2-Multi de una capacidad crítica para actualizar sus configuraciones de conexión a bases de datos en caliente, sin necesidad de reiniciar el servicio. Esto mejora la disponibilidad, simplifica el mantenimiento y previene fugas de recursos al asegurar el cierre ordenado de los pools de conexión antiguos. ' - • Beneficio: Estos cambios dotan al servidor jRDC2-Multi de una capacidad crítica para actualizar sus configuraciones de conexión a bases de datos en caliente, sin necesidad de reiniciar el servicio. Esto mejora la disponibilidad, simplifica el mantenimiento y previene fugas de recursos al asegurar el cierre ordenado de los pools de conexión antiguos.
'
' - VERSION 5.09.13.2 (Ahora consolidado en 5.09.15) ' - VERSION 5.09.13.2 (Ahora consolidado en 5.09.15)
' - Módulo: DBHandlerJSON.bas ' - Módulo: DBHandlerJSON.bas
' - Descripción de Cambios: Manejo de Peticiones POST con Content-Type: application/json ' - Descripción de Cambios: Manejo de Peticiones POST con Content-Type: application/json
@@ -119,7 +99,7 @@ Sub Process_Globals
' - 5. Se corrigió el nombre de la variable Is a Is0 para evitar un conflicto con la palabra reservada Is de B4X . ' - 5. Se corrigió el nombre de la variable Is a Is0 para evitar un conflicto con la palabra reservada Is de B4X .
' - 6. Se actualizó el mensaje de error para aclarar que el JSON puede faltar tanto en el parámetro j como en el cuerpo de la petición. ' - 6. Se actualizó el mensaje de error para aclarar que el JSON puede faltar tanto en el parámetro j como en el cuerpo de la petición.
' - • Beneficio: Esta corrección asegura que el DBHandlerJSON sea compatible con el "Método Recomendado" de POST con application/json, mejorando la robustez y la adherencia a los estándares de las APIs web, Sin comprometer la retrocompatibilidad con el "Método Legacy" (GET con parámetro j). ' - • Beneficio: Esta corrección asegura que el DBHandlerJSON sea compatible con el "Método Recomendado" de POST con application/json, mejorando la robustez y la adherencia a los estándares de las APIs web, Sin comprometer la retrocompatibilidad con el "Método Legacy" (GET con parámetro j).
'
' - VERSION 5.09.13 (Ahora consolidado en 5.09.15) ' - VERSION 5.09.13 (Ahora consolidado en 5.09.15)
' feat: Mejora la inicialización del pool de conexiones y el soporte multi-DB. ' feat: Mejora la inicialización del pool de conexiones y el soporte multi-DB.
' '
@@ -143,7 +123,7 @@ Sub Process_Globals
' * **Configuración de C3P0:** Todas las propiedades del pool (incluyendo `setInitialPoolSize`, `setMinPoolSize`, `setMaxPoolSize`, `setMaxIdleTime`, etc. ahora se aplican mediante `jo.RunMethod` *inmediatamente después* de `pool.Initialize` y *antes* de que el pool intente adquirir conexiones. ' * **Configuración de C3P0:** Todas las propiedades del pool (incluyendo `setInitialPoolSize`, `setMinPoolSize`, `setMaxPoolSize`, `setMaxIdleTime`, etc. ahora se aplican mediante `jo.RunMethod` *inmediatamente después* de `pool.Initialize` y *antes* de que el pool intente adquirir conexiones.
' * **Forzar reportes de errores:** Se añadieron las líneas `jo.RunMethod("setAcquireRetryAttempts", Array As Object(1))` y `jo.RunMethod("setBreakAfterAcquireFailure", Array As Object(True))`. Estas son cruciales para forzar a C3P0 a lanzar una `SQLException` explícita si falla al crear las conexiones iniciales, en lugar de fallar silenciosamente. ' * **Forzar reportes de errores:** Se añadieron las líneas `jo.RunMethod("setAcquireRetryAttempts", Array As Object(1))` y `jo.RunMethod("setBreakAfterAcquireFailure", Array As Object(True))`. Estas son cruciales para forzar a C3P0 a lanzar una `SQLException` explícita si falla al crear las conexiones iniciales, en lugar de fallar silenciosamente.
' * **Activación forzada del pool:** Se implementó `Dim tempCon As SQL = pool.GetConnection` seguido de `tempCon.Close` dentro de un bloque `Try...Catch`. Esto obliga al pool a establecer las conexiones iniciales (`InitialPoolSize`) con la configuración ya aplicada, permitiendo la captura de errores reales si la conexión falla. ' * **Activación forzada del pool:** Se implementó `Dim tempCon As SQL = pool.GetConnection` seguido de `tempCon.Close` dentro de un bloque `Try...Catch`. Esto obliga al pool a establecer las conexiones iniciales (`InitialPoolSize`) con la configuración ya aplicada, permitiendo la captura de errores reales si la conexión falla.
'
' - VERSION 5.09.08 ' - VERSION 5.09.08
' - Se agregó que se puedan configurar en el config.properties los siguientes parametros: ' - Se agregó que se puedan configurar en el config.properties los siguientes parametros:
' - - setInitialPoolSize = 3 ' - - setInitialPoolSize = 3

View File

@@ -33,7 +33,6 @@ Public Sub Handle(req As ServletRequest, resp As ServletResponse)
Try Try
Dim storedHash As String = Main.SQL1.ExecQuerySingleResult2("SELECT password_hash FROM users WHERE username = ?", Array As String(currentUser)) Dim storedHash As String = Main.SQL1.ExecQuerySingleResult2("SELECT password_hash FROM users WHERE username = ?", Array As String(currentUser))
Log("--- Probando con contraseña fija ---")
Log("Valor de la BD (storedHash): " & storedHash) Log("Valor de la BD (storedHash): " & storedHash)
If storedHash = Null Or bc.checkpw(currentPass, storedHash) = False Then ' <<--- CAMBIO CLAVE AQUÍ If storedHash = Null Or bc.checkpw(currentPass, storedHash) = False Then ' <<--- CAMBIO CLAVE AQUÍ
resp.Write("<script>alert('Error: La contraseña actual es incorrecta.'); history.back();</script>") resp.Write("<script>alert('Error: La contraseña actual es incorrecta.'); history.back();</script>")

View File

@@ -17,7 +17,7 @@ Sub Class_Globals
' La siguiente sección de constantes y utilidades se compila condicionalmente ' La siguiente sección de constantes y utilidades se compila condicionalmente
' solo si la directiva #if VERSION1 está activa. Esto es para dar soporte ' solo si la directiva #if VERSION1 está activa. Esto es para dar soporte
' a una versión antigua del protocolo de comunicación de DBRequestManager. ' a una versión antigua del protocolo de comunicación de DBRequestManager.
#if VERSION1 ' #if VERSION1
' Constantes para identificar los tipos de datos en la serialización personalizada (protocolo V1). ' Constantes para identificar los tipos de datos en la serialización personalizada (protocolo V1).
Private const T_NULL = 0, T_STRING = 1, T_SHORT = 2, T_INT = 3, T_LONG = 4, T_FLOAT = 5 _ Private const T_NULL = 0, T_STRING = 1, T_SHORT = 2, T_INT = 3, T_LONG = 4, T_FLOAT = 5 _
,T_DOUBLE = 6, T_BOOLEAN = 7, T_BLOB = 8 As Byte ,T_DOUBLE = 6, T_BOOLEAN = 7, T_BLOB = 8 As Byte
@@ -25,7 +25,7 @@ Sub Class_Globals
Private bc As ByteConverter Private bc As ByteConverter
' Utilidad para comprimir/descomprimir streams de datos (usado en V1). ' Utilidad para comprimir/descomprimir streams de datos (usado en V1).
Private cs As CompressedStreams Private cs As CompressedStreams
#end if ' #end if
' Mapa para convertir tipos de columna JDBC de fecha/hora a los nombres de métodos de Java ' Mapa para convertir tipos de columna JDBC de fecha/hora a los nombres de métodos de Java
' para obtener los valores correctos de ResultSet. ' para obtener los valores correctos de ResultSet.
@@ -68,13 +68,11 @@ Sub Handle(req As ServletRequest, resp As ServletResponse)
' Verifica si el dbKey extraído corresponde a una base de datos configurada y cargada en Main. ' Verifica si el dbKey extraído corresponde a una base de datos configurada y cargada en Main.
If Main.Connectors.ContainsKey(dbKey) = False Then If Main.Connectors.ContainsKey(dbKey) = False Then
' Si la base de datos no es válida, se construye un mensaje de error y se envía.
Dim ErrorMsg As String = $"Invalid DB key specified in URL: '${dbKey}'. Valid keys are: ${Main.listaDeCP}"$ Dim ErrorMsg As String = $"Invalid DB key specified in URL: '${dbKey}'. Valid keys are: ${Main.listaDeCP}"$
Log(ErrorMsg) Log(ErrorMsg)
SendPlainTextError(resp, 400, ErrorMsg) ' Envía una respuesta de error al cliente. Main.LogServerError("ERROR", "DBHandlerB4X.Handle", ErrorMsg, dbKey, Null, req.RemoteAddress) ' <-- Nuevo Log
' No se llama a CleanupAndLog aquí, ya que el contador de peticiones no se ha incrementado SendPlainTextError(resp, 400, ErrorMsg)
' y no se ha obtenido ninguna conexión del pool. Return
Return ' Termina la ejecución del handler.
End If End If
' === FIN DE LA LÓGICA DINÁMICA === ' === FIN DE LA LÓGICA DINÁMICA ===
@@ -130,7 +128,7 @@ Sub Handle(req As ServletRequest, resp As ServletResponse)
CleanupAndLog(dbKey, "error_in_" & method, duration, req.RemoteAddress, requestsBeforeDecrement, poolBusyConnectionsForLog, con) CleanupAndLog(dbKey, "error_in_" & method, duration, req.RemoteAddress, requestsBeforeDecrement, poolBusyConnectionsForLog, con)
Return ' Salida temprana si hay un error. Return ' Salida temprana si hay un error.
End If End If
#if VERSION1 ' #if VERSION1
' Estas ramas se compilan solo si #if VERSION1 está activo (para protocolo antiguo). ' Estas ramas se compilan solo si #if VERSION1 está activo (para protocolo antiguo).
Else if method = "query" Then Else if method = "query" Then
in = cs.WrapInputStream(in, "gzip") ' Descomprime el stream de entrada si es protocolo V1. in = cs.WrapInputStream(in, "gzip") ' Descomprime el stream de entrada si es protocolo V1.
@@ -148,7 +146,7 @@ Sub Handle(req As ServletRequest, resp As ServletResponse)
CleanupAndLog(dbKey, "error_in_" & method, duration, req.RemoteAddress, requestsBeforeDecrement, poolBusyConnectionsForLog, con) CleanupAndLog(dbKey, "error_in_" & method, duration, req.RemoteAddress, requestsBeforeDecrement, poolBusyConnectionsForLog, con)
Return Return
End If End If
#end if ' #end if
Else if method = "batch2" Then Else if method = "batch2" Then
' Ejecuta un lote de comandos (INSERT, UPDATE, DELETE) utilizando el protocolo V2. ' Ejecuta un lote de comandos (INSERT, UPDATE, DELETE) utilizando el protocolo V2.
q = ExecuteBatch2(dbKey, con, in, resp) q = ExecuteBatch2(dbKey, con, in, resp)
@@ -158,18 +156,19 @@ Sub Handle(req As ServletRequest, resp As ServletResponse)
Return ' Salida temprana si hay un error. Return ' Salida temprana si hay un error.
End If End If
Else Else
' Si el método solicitado no es reconocido, se registra un error y se envía una respuesta adecuada. Dim ErrorMsg As String = "Unknown method: " & method
Log("Unknown method: " & method) Log(ErrorMsg)
Main.LogServerError("ERROR", "DBHandlerB4X.Handle", ErrorMsg, dbKey, method, req.RemoteAddress) ' <-- Nuevo Log
SendPlainTextError(resp, 500, "unknown method") SendPlainTextError(resp, 500, "unknown method")
q = "unknown_method_handler" ' Aseguramos un valor para 'q' para que el log sea informativo. q = "unknown_method_handler"
duration = DateTime.Now - start duration = DateTime.Now - start
CleanupAndLog(dbKey, q, duration, req.RemoteAddress, requestsBeforeDecrement, poolBusyConnectionsForLog, con) CleanupAndLog(dbKey, q, duration, req.RemoteAddress, requestsBeforeDecrement, poolBusyConnectionsForLog, con)
Return ' Salida temprana. Return
End If End If
Catch ' --- CATCH: Maneja errores generales de ejecución o de SQL --- Catch ' --- CATCH: Maneja errores generales de ejecución o de SQL ---
' Si ocurre una excepción inesperada durante el procesamiento de la petición.
Log(LastException) ' Registra la excepción completa en el log. Log(LastException) ' Registra la excepción completa en el log.
Main.LogServerError("ERROR", "DBHandlerB4X.Handle", LastException.Message, dbKey, q, req.RemoteAddress) ' <-- Nuevo Log
SendPlainTextError(resp, 500, LastException.Message) ' Envía un error 500 al cliente. SendPlainTextError(resp, 500, LastException.Message) ' Envía un error 500 al cliente.
q = "error_in_b4x_handler" ' Aseguramos un valor para 'q' en caso de excepción. q = "error_in_b4x_handler" ' Aseguramos un valor para 'q' en caso de excepción.
End Try ' --- FIN: Bloque Try principal --- End Try ' --- FIN: Bloque Try principal ---
@@ -238,34 +237,33 @@ Private Sub ExecuteQuery2 (DB As String, con As SQL, in As InputStream, resp As
If sqlCommand = Null Or sqlCommand = "null" Or sqlCommand.Trim = "" Then If sqlCommand = Null Or sqlCommand = "null" Or sqlCommand.Trim = "" Then
Dim errorMessage As String = $"El comando '${cmd.Name}' no fue encontrado en el config.properties de '${DB}'."$ Dim errorMessage As String = $"El comando '${cmd.Name}' no fue encontrado en el config.properties de '${DB}'."$
Log(errorMessage) Log(errorMessage)
Main.LogServerError("ERROR", "DBHandlerB4X.ExecuteQuery2", errorMessage, DB, cmd.Name, Null)
' Envía un error 400 (Bad Request) al cliente informando del problema. ' Envía un error 400 (Bad Request) al cliente informando del problema.
SendPlainTextError(resp, 400, errorMessage) SendPlainTextError(resp, 400, errorMessage)
Return "error" ' Retorna un texto para el log. Return "error" ' Retorna un texto para el log.
End If End If
' <<< FIN NUEVA VALIDACIÓN >>> ' <<< FIN NUEVA VALIDACIÓN >>>
' --- INICIO VALIDACIÓN DE PARÁMETROS --- ' <<< INICIO VALIDACIÓN DE PARÁMETROS CENTRALIZADA >>>
' Comprueba si el SQL espera parámetros o si se recibieron parámetros. ' Convertimos el array de Object() de cmd.Parameters a una List para la utilidad de validación.
If sqlCommand.Contains("?") Or (cmd.Parameters <> Null And cmd.Parameters.Length > 0) Then Dim paramsAsList As List
' Cuenta cuántos '?' hay en la sentencia SQL para saber cuántos parámetros se esperan. paramsAsList.Initialize
Dim expectedParams As Int = sqlCommand.Length - sqlCommand.Replace("?", "").Length If cmd.Parameters <> Null Then
' Cuenta cuántos parámetros se recibieron. For Each p As Object In cmd.Parameters
Dim receivedParams As Int paramsAsList.Add(p)
If cmd.Parameters = Null Then receivedParams = 0 Else receivedParams = cmd.Parameters.Length Next
' Compara el número de parámetros esperados con los recibidos.
If expectedParams <> receivedParams Then
Dim errorMessage As String = $"Número de parametros equivocado para "${cmd.Name}". Se esperaban ${expectedParams} y se recibieron ${receivedParams}."$
Log(errorMessage)
' Si no coinciden, envía un error 400 al cliente.
SendPlainTextError(resp, 400, errorMessage)
Return "error"
End If End If
End If
' --- FIN VALIDACIÓN ---
' Ejecuta la consulta SQL con los parámetros proporcionados. Dim validationResult As ParameterValidationResult = ParameterValidationUtils.ValidateAndAdjustParameters(cmd.Name, DB, sqlCommand, paramsAsList, Connector.IsParameterToleranceEnabled)
Dim rs As ResultSet = con.ExecQuery2(sqlCommand, cmd.Parameters)
If validationResult.Success = False Then
SendPlainTextError(resp, 400, validationResult.ErrorMessage)
Return "error" ' Salida temprana si la validación falla.
End If
' Ejecuta la consulta SQL con la lista de parámetros validada.
Dim rs As ResultSet = con.ExecQuery2(sqlCommand, validationResult.ParamsToExecute)
' <<< FIN VALIDACIÓN DE PARÁMETROS CENTRALIZADA >>>
' Si el límite es 0 o negativo, lo establece a un valor muy alto (máximo entero). ' Si el límite es 0 o negativo, lo establece a un valor muy alto (máximo entero).
If limit <= 0 Then limit = 0x7fffffff 'max int If limit <= 0 Then limit = 0x7fffffff 'max int
@@ -367,28 +365,32 @@ Private Sub ExecuteBatch2(DB As String, con As SQL, in As InputStream, resp As S
con.Rollback ' Deshace la transacción si un comando es inválido. con.Rollback ' Deshace la transacción si un comando es inválido.
Dim errorMessage As String = $"El comando '${cmd.Name}' no fue encontrado en el config.properties de '${DB}'."$ Dim errorMessage As String = $"El comando '${cmd.Name}' no fue encontrado en el config.properties de '${DB}'."$
Log(errorMessage) Log(errorMessage)
Main.LogServerError("ERROR", "DBHandlerB4X.ExecuteBatch2", errorMessage, DB, cmd.Name, Null)
SendPlainTextError(resp, 400, errorMessage) SendPlainTextError(resp, 400, errorMessage)
Return "error" Return "error"
End If End If
' <<< FIN NUEVA VALIDACIÓN >>> ' <<< FIN NUEVA VALIDACIÓN >>>
' --- INICIO VALIDACIÓN DE PARÁMETROS DENTRO DEL BATCH --- ' <<< INICIO VALIDACIÓN DE PARÁMETROS CENTRALIZADA DENTRO DEL BATCH >>>
If sqlCommand.Contains("?") Or (cmd.Parameters <> Null And cmd.Parameters.Length > 0) Then ' Convertimos el array de Object() de cmd.Parameters a una List para la utilidad de validación.
Dim expectedParams As Int = sqlCommand.Length - sqlCommand.Replace("?", "").Length Dim paramsAsList As List
Dim receivedParams As Int paramsAsList.Initialize
If cmd.Parameters = Null Then receivedParams = 0 Else receivedParams = cmd.Parameters.Length If cmd.Parameters <> Null Then
' Si el número de parámetros no coincide, deshace la transacción y envía error. For Each p As Object In cmd.Parameters
If expectedParams <> receivedParams Then paramsAsList.Add(p)
con.Rollback Next
Dim errorMessage As String = $"Número de parametros equivocado para "${cmd.Name}". Se esperaban ${expectedParams} y se recibieron ${receivedParams}."$
Log(errorMessage)
SendPlainTextError(resp, 400, errorMessage)
Return "error"
End If End If
End If
' --- FIN VALIDACIÓN ---
con.ExecNonQuery2(sqlCommand, cmd.Parameters) ' Ejecuta el comando (no es una consulta, no devuelve filas). Dim validationResult As ParameterValidationResult = ParameterValidationUtils.ValidateAndAdjustParameters(cmd.Name, DB, sqlCommand, paramsAsList, Connector.IsParameterToleranceEnabled)
If validationResult.Success = False Then
con.Rollback ' ¡Importante hacer rollback si la validación falla dentro de una transacción!
SendPlainTextError(resp, 400, validationResult.ErrorMessage)
Return "error" ' Salida temprana si la validación falla.
End If
con.ExecNonQuery2(sqlCommand, validationResult.ParamsToExecute) ' Ejecuta el comando con la lista de parámetros validada.
' <<< FIN VALIDACIÓN DE PARÁMETROS CENTRALIZADA DENTRO DEL BATCH >>>
Next Next
res.Rows.Add(Array As Object(0)) ' Añade una fila simbólica al resultado para indicar éxito. res.Rows.Add(Array As Object(0)) ' Añade una fila simbólica al resultado para indicar éxito.
@@ -397,6 +399,7 @@ Private Sub ExecuteBatch2(DB As String, con As SQL, in As InputStream, resp As S
' Si cualquier comando falla, se captura el error. ' Si cualquier comando falla, se captura el error.
con.Rollback ' Se deshacen todos los cambios hechos en la transacción. con.Rollback ' Se deshacen todos los cambios hechos en la transacción.
Log(LastException) ' Registra la excepción. Log(LastException) ' Registra la excepción.
Main.LogServerError("ERROR", "DBHandlerB4X.ExecuteBatch2", LastException.Message, DB, "batch_execution_error", Null)
SendPlainTextError(resp, 500, LastException.Message) ' Envía un error 500 al cliente. SendPlainTextError(resp, 500, LastException.Message) ' Envía un error 500 al cliente.
End Try End Try
@@ -410,7 +413,7 @@ End Sub
' --- Subrutinas para manejar la ejecución de queries y batches (Protocolo V1 - Compilación Condicional) --- ' --- Subrutinas para manejar la ejecución de queries y batches (Protocolo V1 - Compilación Condicional) ---
' Este código se compila solo si #if VERSION1 está activo, para mantener compatibilidad con clientes antiguos. ' Este código se compila solo si #if VERSION1 está activo, para mantener compatibilidad con clientes antiguos.
#if VERSION1 '#if VERSION1
' Ejecuta un lote de comandos usando el protocolo V1. ' Ejecuta un lote de comandos usando el protocolo V1.
Private Sub ExecuteBatch(DB As String, con As SQL, in As InputStream, resp As ServletResponse) As String Private Sub ExecuteBatch(DB As String, con As SQL, in As InputStream, resp As ServletResponse) As String
@@ -434,28 +437,23 @@ Private Sub ExecuteBatch(DB As String, con As SQL, in As InputStream, resp As Se
con.Rollback ' Deshace la transacción si un comando es inválido. con.Rollback ' Deshace la transacción si un comando es inválido.
Dim errorMessage As String = $"El comando '${queryName}' no fue encontrado en el config.properties de '${DB}'."$ Dim errorMessage As String = $"El comando '${queryName}' no fue encontrado en el config.properties de '${DB}'."$
Log(errorMessage) Log(errorMessage)
Main.LogServerError("ERROR", "DBHandlerB4X.ExecuteBatch (V1)", errorMessage, DB, queryName, Null)
SendPlainTextError(resp, 400, errorMessage) SendPlainTextError(resp, 400, errorMessage)
Return "error" Return "error"
End If End If
' <<< FIN NUEVA VALIDACIÓN >>> ' <<< FIN NUEVA VALIDACIÓN >>>
' --- INICIO VALIDACIÓN DE PARÁMETROS DENTRO DEL BATCH (V1) --- ' <<< INICIO VALIDACIÓN DE PARÁMETROS CENTRALIZADA DENTRO DEL BATCH (V1) >>>
If sqlCommand.Contains("?") Or (params <> Null And params.Size > 0) Then Dim validationResult As ParameterValidationResult = ParameterValidationUtils.ValidateAndAdjustParameters(queryName, DB, sqlCommand, params, Connector.IsParameterToleranceEnabled)
Dim expectedParams As Int = sqlCommand.Length - sqlCommand.Replace("?", "").Length
Dim receivedParams As Int
If params = Null Then receivedParams = 0 Else receivedParams = params.Size
If expectedParams <> receivedParams Then If validationResult.Success = False Then
con.Rollback con.Rollback ' ¡Importante hacer rollback si la validación falla dentro de una transacción!
Dim errorMessage As String = $"Número de parametros equivocado para "${queryName}". Se esperaban ${expectedParams} y se recibieron ${receivedParams}."$ SendPlainTextError(resp, 400, validationResult.ErrorMessage)
Log(errorMessage) Return "error" ' Salida temprana si la validación falla.
SendPlainTextError(resp, 400, errorMessage)
Return "error"
End If End If
End If
' --- FIN VALIDACIÓN ---
con.ExecNonQuery2(sqlCommand, params) ' Ejecuta el comando. con.ExecNonQuery2(sqlCommand, validationResult.ParamsToExecute) ' Ejecuta el comando con la lista de parámetros validada.
' <<< FIN VALIDACIÓN DE PARÁMETROS CENTRALIZADA DENTRO DEL BATCH (V1) >>>
Next Next
con.TransactionSuccessful ' Confirma la transacción. con.TransactionSuccessful ' Confirma la transacción.
@@ -473,6 +471,7 @@ Private Sub ExecuteBatch(DB As String, con As SQL, in As InputStream, resp As Se
Catch Catch
con.Rollback con.Rollback
Log(LastException) Log(LastException)
Main.LogServerError("ERROR", "DBHandlerB4X.ExecuteBatch (V1)", LastException.Message, DB, "batch_execution_error_v1", Null)
SendPlainTextError(resp, 500, LastException.Message) SendPlainTextError(resp, 500, LastException.Message)
End Try End Try
@@ -495,28 +494,23 @@ Private Sub ExecuteQuery(DB As String, con As SQL, in As InputStream, resp As Se
If theSql = Null Or theSql ="null" Or theSql.Trim = "" Then If theSql = Null Or theSql ="null" Or theSql.Trim = "" Then
Dim errorMessage As String = $"El comando '${queryName}' no fue encontrado en el config.properties de '${DB}'."$ Dim errorMessage As String = $"El comando '${queryName}' no fue encontrado en el config.properties de '${DB}'."$
Log(errorMessage) Log(errorMessage)
Main.LogServerError("ERROR", "DBHandlerB4X.ExecuteQuery (V1)", errorMessage, DB, queryName, Null)
SendPlainTextError(resp, 400, errorMessage) SendPlainTextError(resp, 400, errorMessage)
Return "error" Return "error"
End If End If
' <<< FIN NUEVA VALIDACIÓN >>> ' <<< FIN NUEVA VALIDACIÓN >>>
' --- INICIO VALIDACIÓN DE PARÁMETROS (V1) --- ' <<< INICIO VALIDACIÓN DE PARÁMETROS CENTRALIZADA (V1) >>>
If theSql.Contains("?") Or (params <> Null And params.Size > 0) Then Dim validationResult As ParameterValidationResult = ParameterValidationUtils.ValidateAndAdjustParameters(queryName, DB, theSql, params, Connector.IsParameterToleranceEnabled)
Dim expectedParams As Int = theSql.Length - theSql.Replace("?", "").Length
Dim receivedParams As Int
If params = Null Then receivedParams = 0 Else receivedParams = params.Size
If expectedParams <> receivedParams Then If validationResult.Success = False Then
Dim errorMessage As String = $"Número de parametros equivocado para "${queryName}". Se esperaban ${expectedParams} y se recibieron ${receivedParams}."$ SendPlainTextError(resp, 400, validationResult.ErrorMessage)
Log(errorMessage) Return "error" ' Salida temprana si la validación falla.
SendPlainTextError(resp, 400, errorMessage)
Return "error"
End If End If
End If
' --- FIN VALIDACIÓN ---
' Ejecuta la consulta. ' Ejecuta la consulta con la lista de parámetros validada.
Dim rs As ResultSet = con.ExecQuery2(theSql, params) Dim rs As ResultSet = con.ExecQuery2(theSql, validationResult.ParamsToExecute)
' <<< FIN VALIDACIÓN DE PARÁMETROS CENTRALIZADA (V1) >>>
If limit <= 0 Then limit = 0x7fffffff 'max int If limit <= 0 Then limit = 0x7fffffff 'max int
@@ -691,7 +685,7 @@ Private Sub ReadList(in As InputStream) As List
Return l1 Return l1
End Sub End Sub
#end If ' Fin del bloque de compilación condicional para VERSION1 '#end If ' Fin del bloque de compilación condicional para VERSION1
' Envía una respuesta de error en formato de texto plano. ' Envía una respuesta de error en formato de texto plano.
' Esto evita la página de error HTML por defecto que genera resp.SendError. ' Esto evita la página de error HTML por defecto que genera resp.SendError.
@@ -716,6 +710,8 @@ Private Sub SendPlainTextError(resp As ServletResponse, statusCode As Int, error
Catch Catch
' Si algo falla al intentar enviar la respuesta de error, lo registra en el log ' Si algo falla al intentar enviar la respuesta de error, lo registra en el log
' para que no se pierda la causa original del problema. ' para que no se pierda la causa original del problema.
Log("Error sending plain text error response: " & LastException) Dim ErrorMsg As String = "Error sending plain text error response: " & LastException
Log(ErrorMsg)
Main.LogServerError("ERROR", "DBHandlerB4X.SendPlainTextError", ErrorMsg, Null, Null, Null)
End Try End Try
End Sub End Sub

View File

@@ -63,11 +63,12 @@ Sub Handle(req As ServletRequest, resp As ServletResponse)
' Validación inicial: Si no hay JSON, se envía un error 400. ' Validación inicial: Si no hay JSON, se envía un error 400.
If jsonString = Null Or jsonString = "" Then If jsonString = Null Or jsonString = "" Then
SendErrorResponse(resp, 400, "Falta el parámetro 'j' en el URL o el cuerpo JSON en la petición.") Dim ErrorMsg As String = "Falta el parámetro 'j' en el URL o el cuerpo JSON en la petición."
SendErrorResponse(resp, 400, ErrorMsg)
Main.LogServerError("ERROR", "DBHandlerJSON.Handle", ErrorMsg, finalDbKey, queryNameForLog, req.RemoteAddress) ' <-- Nuevo Log
duration = DateTime.Now - start duration = DateTime.Now - start
' Llama a CleanupAndLog para registrar que hubo un error, pero con contadores a 0 o inicializados.
CleanupAndLog(finalDbKey, queryNameForLog, duration, req.RemoteAddress, requestsBeforeDecrement, poolBusyConnectionsForLog, con) CleanupAndLog(finalDbKey, queryNameForLog, duration, req.RemoteAddress, requestsBeforeDecrement, poolBusyConnectionsForLog, con)
Return ' Salida temprana si no hay JSON válido. Return
End If End If
Dim parser As JSONParser Dim parser As JSONParser
@@ -108,10 +109,12 @@ Sub Handle(req As ServletRequest, resp As ServletResponse)
' Validación: Si el dbKey no es válido o no está configurado en Main.listaDeCP. ' Validación: Si el dbKey no es válido o no está configurado en Main.listaDeCP.
If Main.listaDeCP.IndexOf(finalDbKey) = -1 Then If Main.listaDeCP.IndexOf(finalDbKey) = -1 Then
SendErrorResponse(resp, 400, "Parámetro 'DB' inválido. El nombre '" & finalDbKey & "' no es válido.") Dim ErrorMsg As String = "Parámetro 'DB' inválido. El nombre '" & finalDbKey & "' no es válido."
SendErrorResponse(resp, 400, ErrorMsg)
Main.LogServerError("ERROR", "DBHandlerJSON.Handle", ErrorMsg, finalDbKey, queryNameForLog, req.RemoteAddress) ' <-- Nuevo Log
duration = DateTime.Now - start duration = DateTime.Now - start
CleanupAndLog(finalDbKey, queryNameForLog, duration, req.RemoteAddress, requestsBeforeDecrement, poolBusyConnectionsForLog, con) CleanupAndLog(finalDbKey, queryNameForLog, duration, req.RemoteAddress, requestsBeforeDecrement, poolBusyConnectionsForLog, con)
Return ' Salida temprana si la DB no es válida. Return
End If End If
con = Connector.GetConnection(finalDbKey) ' ¡La conexión a la BD se obtiene aquí del pool de conexiones! con = Connector.GetConnection(finalDbKey) ' ¡La conexión a la BD se obtiene aquí del pool de conexiones!
@@ -135,30 +138,29 @@ Sub Handle(req As ServletRequest, resp As ServletResponse)
If sqlCommand = Null Or sqlCommand = "null" Or sqlCommand.Trim = "" Then If sqlCommand = Null Or sqlCommand = "null" Or sqlCommand.Trim = "" Then
Dim errorMessage As String = $"El comando '${queryNameForLog}' no fue encontrado en el config.properties de '${finalDbKey}'."$ Dim errorMessage As String = $"El comando '${queryNameForLog}' no fue encontrado en el config.properties de '${finalDbKey}'."$
Log(errorMessage) Log(errorMessage)
Main.LogServerError("ERROR", "DBHandlerJSON.Handle", errorMessage, finalDbKey, queryNameForLog, req.RemoteAddress) ' <-- Nuevo Log
SendErrorResponse(resp, 400, errorMessage) SendErrorResponse(resp, 400, errorMessage)
duration = DateTime.Now - start duration = DateTime.Now - start
CleanupAndLog(finalDbKey, queryNameForLog, duration, req.RemoteAddress, requestsBeforeDecrement, poolBusyConnectionsForLog, con) CleanupAndLog(finalDbKey, queryNameForLog, duration, req.RemoteAddress, requestsBeforeDecrement, poolBusyConnectionsForLog, con)
Return
End If
' --- Lógica para ejecutar diferentes tipos de comandos basados en el parámetro 'execType' ---
If execType.ToLowerCase = "executequery" Then
' --- INICIO VALIDACIÓN DE PARÁMETROS CENTRALIZADA ---
Dim validationResult As ParameterValidationResult = ParameterValidationUtils.ValidateAndAdjustParameters(queryNameForLog, finalDbKey, sqlCommand, paramsList, Connector.IsParameterToleranceEnabled)
If validationResult.Success = False Then
SendErrorResponse(resp, 400, validationResult.ErrorMessage)
duration = DateTime.Now - start
CleanupAndLog(finalDbKey, queryNameForLog, duration, req.RemoteAddress, requestsBeforeDecrement, poolBusyConnectionsForLog, con)
Return ' Salida temprana. Return ' Salida temprana.
End If End If
' --- Lógica para ejecutar diferentes tipos de comandos basados en el parámetro 'execType' ---
If execType.ToLowerCase = "executequery" Then
Dim rs As ResultSet Dim rs As ResultSet
' Validación de parámetros para ExecuteQuery. ' Ejecuta la consulta SQL con la lista de parámetros validada.
If sqlCommand.Contains("?") Or paramsList.Size > 0 Then rs = con.ExecQuery2(sqlCommand, validationResult.ParamsToExecute)
Dim expectedParams As Int = sqlCommand.Length - sqlCommand.Replace("?", "").Length ' --- FIN VALIDACIÓN DE PARÁMETROS CENTRALIZADA ---
Dim receivedParams As Int = paramsList.Size
Log($"expectedParams: ${expectedParams}, receivedParams: ${receivedParams}"$)
If expectedParams <> receivedParams Then
SendErrorResponse(resp, 400, $"Número de parámetros equivocado para '${queryNameForLog}'. Se esperaban ${expectedParams} y se recibieron ${receivedParams}."$)
duration = DateTime.Now - start
CleanupAndLog(finalDbKey, queryNameForLog, duration, req.RemoteAddress, requestsBeforeDecrement, poolBusyConnectionsForLog, con)
Return ' Salida temprana.
End If
rs = con.ExecQuery2(sqlCommand, paramsList) ' Ejecuta la consulta con parámetros.
Else
rs = con.ExecQuery(sqlCommand) ' Ejecuta la consulta sin parámetros.
End If
Dim ResultList As List Dim ResultList As List
ResultList.Initialize ' Lista para almacenar los resultados de la consulta. ResultList.Initialize ' Lista para almacenar los resultados de la consulta.
@@ -180,29 +182,29 @@ Sub Handle(req As ServletRequest, resp As ServletResponse)
SendSuccessResponse(resp, CreateMap("result": ResultList)) ' Envía la respuesta JSON de éxito. SendSuccessResponse(resp, CreateMap("result": ResultList)) ' Envía la respuesta JSON de éxito.
Else If execType.ToLowerCase = "executecommand" Then Else If execType.ToLowerCase = "executecommand" Then
' Validación de parámetros para ExecuteCommand. ' --- INICIO VALIDACIÓN DE PARÁMETROS CENTRALIZADA ---
If sqlCommand.Contains("?") Then Dim validationResult As ParameterValidationResult = ParameterValidationUtils.ValidateAndAdjustParameters(queryNameForLog, finalDbKey, sqlCommand, paramsList, Connector.IsParameterToleranceEnabled)
Dim expectedParams As Int = sqlCommand.Length - sqlCommand.Replace("?", "").Length
Dim receivedParams As Int = paramsList.Size If validationResult.Success = False Then
If expectedParams <> receivedParams Then SendErrorResponse(resp, 400, validationResult.ErrorMessage)
SendErrorResponse(resp, 400, $"Número de parámetros equivocado para '${queryNameForLog}'. Se esperaban ${expectedParams} y se recibieron ${receivedParams}."$)
duration = DateTime.Now - start duration = DateTime.Now - start
CleanupAndLog(finalDbKey, queryNameForLog, duration, req.RemoteAddress, requestsBeforeDecrement, poolBusyConnectionsForLog, con) CleanupAndLog(finalDbKey, queryNameForLog, duration, req.RemoteAddress, requestsBeforeDecrement, poolBusyConnectionsForLog, con)
Return ' Salida temprana. Return ' Salida temprana.
End If End If
End If
con.ExecNonQuery2(sqlCommand, paramsList) ' Ejecuta un comando (INSERT, UPDATE, DELETE).
SendSuccessResponse(resp, CreateMap("message": "Command executed successfully")) ' Envía confirmación de éxito.
con.ExecNonQuery2(sqlCommand, validationResult.ParamsToExecute) ' Ejecuta un comando con la lista de parámetros validada.
SendSuccessResponse(resp, CreateMap("message": "Command executed successfully")) ' Envía confirmación de éxito.
' --- FIN VALIDACIÓN DE PARÁMETROS CENTRALIZADA ---
Else Else
' Si el tipo de ejecución no es reconocido. Dim ErrorMsg As String = "Parámetro 'exec' inválido. '" & execType & "' no es un valor permitido."
SendErrorResponse(resp, 400, "Parámetro 'exec' inválido. '" & execType & "' no es un valor permitido.") SendErrorResponse(resp, 400, ErrorMsg)
Main.LogServerError("ERROR", "DBHandlerJSON.Handle", ErrorMsg, finalDbKey, queryNameForLog, req.RemoteAddress) ' <-- Nuevo Log
' El flujo continúa hasta la limpieza final si no hay un Return explícito. ' El flujo continúa hasta la limpieza final si no hay un Return explícito.
End If End If
Catch ' --- CATCH: Maneja errores generales de ejecución o de SQL/JSON --- Catch ' --- CATCH: Maneja errores generales de ejecución o de SQL/JSON ---
' Si ocurre una excepción inesperada durante el procesamiento de la petición.
Log(LastException) ' Registra la excepción completa en el log. Log(LastException) ' Registra la excepción completa en el log.
Main.LogServerError("ERROR", "DBHandlerJSON.Handle", LastException.Message, finalDbKey, queryNameForLog, req.RemoteAddress) ' <-- Nuevo Log
SendErrorResponse(resp, 500, LastException.Message) ' Envía un error 500 al cliente. SendErrorResponse(resp, 500, LastException.Message) ' Envía un error 500 al cliente.
queryNameForLog = "error_processing_json" ' Para registrar que hubo un error en el log. queryNameForLog = "error_processing_json" ' Para registrar que hubo un error en el log.
End Try ' --- FIN: Bloque Try principal --- End Try ' --- FIN: Bloque Try principal ---

View File

@@ -11,13 +11,19 @@ DriverClass=oracle.jdbc.driver.OracleDriver
#GOHAN ---> server #GOHAN ---> server
#JdbcUrl=jdbc:oracle:thin:@//10.0.0.205:1521/DBKMT #JdbcUrl=jdbc:oracle:thin:@//10.0.0.205:1521/DBKMT
#JdbcUrl=jdbc:oracle:thin:@//10.0.0.236:1521/DBKMT #JdbcUrl=jdbc:oracle:thin:@//10.0.0.236:1521/DBKMT
JdbcUrl=jdbc:oracle:thin:@//192.168.101.13:1521/DBKMT?v$session.program=jRDC_Multi JdbcUrl=jdbc:oracle:thin:@//192.168.101.13:1521/DBKMT?v$session.program=jRDC_MultiSALMA
# Configuración del pool de conexiones para DB2 # Configuración del pool de conexiones
InitialPoolSize=3 InitialPoolSize=3
MinPoolSize=2 MinPoolSize=2
MaxPoolSize=10 MaxPoolSize=15
AcquireIncrement=5 AcquireIncrement=2
# Configuración de tolerancia de parámetros:
# 1 = Habilita la tolerancia a parámetros de más (se recortarán los excesivos).
# 0 = Deshabilita la tolerancia (el servidor será estricto y lanzará un error si hay parámetros de más).
# Por defecto, si no se especifica o el valor es diferente de 1, la tolerancia estará DESHABILITADA (modo estricto).
parameterTolerance=1
# SVR-KEYMON-PRODUCCION--> Usuario # SVR-KEYMON-PRODUCCION--> Usuario
User=SALMA User=SALMA
@@ -50,7 +56,7 @@ Debug=true
sql.traeConexion=select 'DB2' as conexion from dual sql.traeConexion=select 'DB2' as conexion from dual
sql.select_soporte=select * from GUNA.soporte sql.select_soporte=select * from GUNA.soporte
sql.select_conexion=SELECT 'OK' AS VALOR FROM DUAL sql.select_conexion=SELECT 'OK' AS VALOR FROM DUAL
sql.traeConexion4=SELECT (?) AS p1, (?) AS p2, (?) AS p3 FROM DUAL sql.traeConexion4=SELECT 'DB2' as db, (?) AS p1, (?) AS p2, (?) AS p3 FROM DUAL
sql.select_version=select cat_ve_version from cat_version sql.select_version=select cat_ve_version from cat_version
sql.select_version_GV2=select cat_ve_version from GUNA.cat_version sql.select_version_GV2=select cat_ve_version from GUNA.cat_version
sql.selectAlmacen=select * from cat_almacen where cat_al_id = ? sql.selectAlmacen=select * from cat_almacen where cat_al_id = ?

View File

@@ -11,20 +11,21 @@ DriverClass=oracle.jdbc.driver.OracleDriver
#GOHAN ---> server #GOHAN ---> server
#JdbcUrl=jdbc:oracle:thin:@//10.0.0.205:1521/DBKMT #JdbcUrl=jdbc:oracle:thin:@//10.0.0.205:1521/DBKMT
#JdbcUrl=jdbc:oracle:thin:@//10.0.0.236:1521/DBKMT #JdbcUrl=jdbc:oracle:thin:@//10.0.0.236:1521/DBKMT
JdbcUrl=jdbc:oracle:thin:@//192.168.101.12:1521/DBKMT?v$session.program=jRDC_Multi JdbcUrl=jdbc:oracle:thin:@//192.168.101.13:1521/DBKMT?v$session.program=jRDC_MultiSALMA
# Configuración del pool de conexiones para DB2 # Configuración del pool de conexiones
InitialPoolSize=3 InitialPoolSize=3
MinPoolSize=2 MinPoolSize=2
MaxPoolSize=10 MaxPoolSize=15
AcquireIncrement=5 AcquireIncrement=2
# SVR-KEYMON-PRODUCCION--> Usuario # SVR-KEYMON-PRODUCCION--> Usuario
#User=GUNA User=SALMA
#Password=GUNAD2015M Password=SALMAD2016M
#User=TORRADOCONAUTO
#Password=TORRADOCONAUTOD2016M
User=TORRADOCONAUTO
Password=TORRADOCONAUTOD2016M
#--> Puertos #--> Puertos
#SAC - DFR - MDA / GOHAN -->COBRANZA #SAC - DFR - MDA / GOHAN -->COBRANZA
@@ -46,8 +47,19 @@ Debug=true
################# #################
################## ##################
sql.traeConexion=select 'DB3' as conexion from dual sql.traeConexion=select 'DB2' as conexion from dual
sql.select_soporte=select * from GUNA.soporte sql.select_soporte=select * from GUNA.soporte
sql.select_conexion=SELECT 'OK' AS VALOR FROM DUAL
sql.traeConexion4=SELECT 'DB2' as db, (?) AS p1, (?) AS p2, (?) AS p3 FROM DUAL
sql.select_version=select cat_ve_version from cat_version
sql.select_version_GV2=select cat_ve_version from GUNA.cat_version
sql.selectAlmacen=select * from cat_almacen where cat_al_id = ?
sql.sv=select * from cat_rutas where CAT_RU_RUTA = ?
sql.verify_device=select * from kelloggs.GUIDs where almacen = ? and ruta = ?
sql.registarMovil=insert into kelloggs.GUIDs (almacen, ruta, guid, estatus) values (?, ?, ?, 'ok')
sql.update_usuario_guna_nobajas=UPDATE GUNA.CAT_LOGINS SET CAT_LO_ESTATUS = 'Activo',CAT_LO_CONECTADO ='0' WHERE CAT_LO_ESTATUS != 'Baja' and CAT_LO_USUARIO = (?)
sql.proc_usuario=BEGIN EXECUTE IMMEDIATE ('DECLARE Cursor_SYS Sys_Refcursor; BEGIN SP_ACTIVAR_USUARIO( '''||(?)||''',Cursor_SYS); end;'); END;
sql.select_almacenes_KELL=select CAT_AG_ID, CAT_AG_NOMBRE from KELLOGGS.cat_agencias order by CAT_AG_NOMBRE sql.select_almacenes_KELL=select CAT_AG_ID, CAT_AG_NOMBRE from KELLOGGS.cat_agencias order by CAT_AG_NOMBRE
sql.select_almacenes_GUNA=select CAT_AG_ID, CAT_AG_NOMBRE from GUNA.cat_agencias order by CAT_AG_NOMBRE sql.select_almacenes_GUNA=select CAT_AG_ID, CAT_AG_NOMBRE from GUNA.cat_agencias order by CAT_AG_NOMBRE

View File

@@ -11,13 +11,13 @@ DriverClass=oracle.jdbc.driver.OracleDriver
#GOHAN ---> server #GOHAN ---> server
#JdbcUrl=jdbc:oracle:thin:@//10.0.0.205:1521/DBKMT #JdbcUrl=jdbc:oracle:thin:@//10.0.0.205:1521/DBKMT
#JdbcUrl=jdbc:oracle:thin:@//10.0.0.236:1521/DBKMT #JdbcUrl=jdbc:oracle:thin:@//10.0.0.236:1521/DBKMT
JdbcUrl=jdbc:oracle:thin:@//192.168.101.13:1521/DBKMT?v$session.program=jRDC_Multi JdbcUrl=jdbc:oracle:thin:@//192.168.101.13:1521/DBKMT?v$session.program=jRDC_MultiSALMA
# Configuración del pool de conexiones para DB2 # Configuración del pool de conexiones
InitialPoolSize=3 InitialPoolSize=3
MinPoolSize=2 MinPoolSize=2
MaxPoolSize=10 MaxPoolSize=15
AcquireIncrement=5 AcquireIncrement=2
# SVR-KEYMON-PRODUCCION--> Usuario # SVR-KEYMON-PRODUCCION--> Usuario
User=SALMA User=SALMA
@@ -31,7 +31,7 @@ Password=SALMAD2016M
#SAC - DFR - MDA / GOHAN -->COBRANZA #SAC - DFR - MDA / GOHAN -->COBRANZA
#ServerPort=1783 #ServerPort=1783
#GUNA - SALMA - DURAKELO - DBC / SVR-KEYMON-PRODUCCION --> DISTRIBUIDORAS #GUNA - SALMA - DURAKELO - DBC / SVR-KEYMON-PRODUCCION --> DISTRIBUIDORAS
ServerPort=9000 ServerPort=9010
#CMG - TORRADO / TRUNKS -->COBRANZA/ GM #CMG - TORRADO / TRUNKS -->COBRANZA/ GM
#ServerPort=1781 #ServerPort=1781
@@ -47,9 +47,19 @@ Debug=true
################# #################
################## ##################
sql.traeConexion=select 'DB4' as conexion from dual sql.traeConexion=select 'DB2' as conexion from dual
sql.select_soporte=select * from GUNA.soporte sql.select_soporte=select * from GUNA.soporte
sql.select_conexion=SELECT 'OK' AS VALOR FROM DUAL sql.select_conexion=SELECT 'OK' AS VALOR FROM DUAL
sql.traeConexion4=SELECT 'DB2' as db, (?) AS p1, (?) AS p2, (?) AS p3 FROM DUAL
sql.select_version=select cat_ve_version from cat_version
sql.select_version_GV2=select cat_ve_version from GUNA.cat_version
sql.selectAlmacen=select * from cat_almacen where cat_al_id = ?
sql.sv=select * from cat_rutas where CAT_RU_RUTA = ?
sql.verify_device=select * from kelloggs.GUIDs where almacen = ? and ruta = ?
sql.registarMovil=insert into kelloggs.GUIDs (almacen, ruta, guid, estatus) values (?, ?, ?, 'ok')
sql.update_usuario_guna_nobajas=UPDATE GUNA.CAT_LOGINS SET CAT_LO_ESTATUS = 'Activo',CAT_LO_CONECTADO ='0' WHERE CAT_LO_ESTATUS != 'Baja' and CAT_LO_USUARIO = (?)
sql.proc_usuario=BEGIN EXECUTE IMMEDIATE ('DECLARE Cursor_SYS Sys_Refcursor; BEGIN SP_ACTIVAR_USUARIO( '''||(?)||''',Cursor_SYS); end;'); END;
sql.select_almacenes_KELL=select CAT_AG_ID, CAT_AG_NOMBRE from KELLOGGS.cat_agencias order by CAT_AG_NOMBRE sql.select_almacenes_KELL=select CAT_AG_ID, CAT_AG_NOMBRE from KELLOGGS.cat_agencias order by CAT_AG_NOMBRE
sql.select_almacenes_GUNA=select CAT_AG_ID, CAT_AG_NOMBRE from GUNA.cat_agencias order by CAT_AG_NOMBRE sql.select_almacenes_GUNA=select CAT_AG_ID, CAT_AG_NOMBRE from GUNA.cat_agencias order by CAT_AG_NOMBRE

View File

@@ -13,11 +13,17 @@ DriverClass=oracle.jdbc.driver.OracleDriver
#JdbcUrl=jdbc:oracle:thin:@//10.0.0.236:1521/DBKMT #JdbcUrl=jdbc:oracle:thin:@//10.0.0.236:1521/DBKMT
JdbcUrl=jdbc:oracle:thin:@//192.168.101.10:1521/DBKMT?v$session.program=jRDC_Multi JdbcUrl=jdbc:oracle:thin:@//192.168.101.10:1521/DBKMT?v$session.program=jRDC_Multi
# Configuración del pool de conexiones para DB1 # Configuración del pool de conexiones
InitialPoolSize=3 InitialPoolSize=3
MinPoolSize=2 MinPoolSize=2
MaxPoolSize=10 MaxPoolSize=15
AcquireIncrement=5 AcquireIncrement=2
# Configuración de tolerancia de parámetros:
# 1 = Habilita la tolerancia a parámetros de más (se recortarán los excesivos).
# 0 = Deshabilita la tolerancia (el servidor será estricto y lanzará un error si hay parámetros de más).
# Por defecto, si no se especifica o el valor es diferente de 1, la tolerancia estará DESHABILITADA (modo estricto).
parameterTolerance=1
# SVR-KEYMON-PRODUCCION--> Usuario # SVR-KEYMON-PRODUCCION--> Usuario
User=GUNA User=GUNA
@@ -52,7 +58,7 @@ sql.select_revisaClienteCredito_GUNA2=select (select count(CAT_CL_CODIGO) from G
sql.traeConexion=select 'DB1' as conexion from dual sql.traeConexion=select 'DB1' as conexion from dual
sql.traeConexion2=select 'DB1' as conexion from dual sql.traeConexion2=select 'DB1' as conexion from dual
sql.traeConexion3=select '1' as par1, 2 as par2 from dual sql.traeConexion3=select '1' as par1, 2 as par2 from dual
sql.traeConexion4=SELECT (?) AS p1, (?) AS p2, (?) AS p3 FROM DUAL sql.traeConexion4=SELECT 'DB1' as db, (?) AS p1, (?) AS p2, (?) AS p3 FROM DUAL
sql.select_soporte=select * from GUNA.soporte sql.select_soporte=select * from GUNA.soporte
sql.select_conexion=SELECT 'OK' AS VALOR FROM DUAL sql.select_conexion=SELECT 'OK' AS VALOR FROM DUAL
sql.selectAlmacen=select cat_al_id, cat_al_desc, cat_al_archftp from cat_almacen where cat_al_id = ? sql.selectAlmacen=select cat_al_id, cat_al_desc, cat_al_archftp from cat_almacen where cat_al_id = ?

View File

@@ -380,112 +380,3 @@ Sub Handle(req As ServletRequest, resp As ServletResponse)
If GlobalParameters.mpLogs.IsInitialized Then GlobalParameters.mpLogs.Put(Command, "Manager : " & Command & " - Time : " & DateTime.Time(DateTime.Now)) If GlobalParameters.mpLogs.IsInitialized Then GlobalParameters.mpLogs.Put(Command, "Manager : " & Command & " - Time : " & DateTime.Time(DateTime.Now))
End Sub End Sub
Sub Handle0(req As ServletRequest, resp As ServletResponse)
' 1. --- Bloque de Seguridad (se mantiene igual) ---
If req.GetSession.GetAttribute2("user_is_authorized", False) = False Then
resp.SendRedirect("/login")
Return
End If
Dim Command As String = req.GetParameter("command")
If Command = "" Then Command = "ping"
Log($"Command: ${Command}"$)
resp.ContentType = "text/html"
' 2. --- Construimos la ESTRUCTURA de la página ---
Dim sb As StringBuilder
sb.Initialize
' Estilos para la página
sb.Append("<html><head><style>")
sb.Append("body {font-family: sans-serif; margin: 2em; background-color: #f9f9f9;} ")
sb.Append("h1, h2 {color: #333;} hr {margin: 2em 0; border: 0; border-top: 1px solid #ddd;} ")
sb.Append("form {background: #f0f0f0; padding: 1.5em; border-radius: 8px; max-width: 400px; margin-bottom: 2em;} ")
sb.Append("input {display: block; width: 95%; padding: 8px; margin-bottom: 10px; border: 1px solid #ccc; border-radius: 4px;} ")
sb.Append("button {padding: 10px 15px; border: none; background-color: #007bff; color: white; cursor: pointer; border-radius: 4px;} ")
sb.Append(".nav a, .logout a {text-decoration: none; margin-right: 10px; color: #007bff;} ")
sb.Append(".output {background: #fff; padding: 1em; border: 1px solid #eee; border-radius: 8px;} ")
sb.Append("</style></head><body>")
' Cabecera y bienvenida
sb.Append("<h1>Panel de Administración jRDC</h1>")
sb.Append($"Bienvenido, <b>${req.GetSession.GetAttribute("username")}</b><br>"$)
' Menú de navegación (se define una sola vez)
sb.Append("<p class='nav'>")
sb.Append($"<a href="/test">Test</a> | <a href="/manager?command=ping">Ping</a> | <a href="/manager?command=reload">Reload</a> | <a href="/manager?command=rpm2">Reiniciar (pm2)</a> | <a href="/manager?command=reviveBow">Revive Bow</a>"$)
sb.Append("</p>")
' Formulario para cambiar contraseña
sb.Append("<hr>")
sb.Append("<h2>Cambiar Contraseña</h2>")
sb.Append("<form action='/changepass' method='post'>")
sb.Append("Contraseña Actual: <input type='password' name='current_password' required><br>")
sb.Append("Nueva Contraseña: <input type='password' name='new_password' required><br>")
sb.Append("Confirmar Nueva Contraseña: <input type='password' name='confirm_password' required><br>")
sb.Append("<button type='submit'>Actualizar Contraseña</button>")
sb.Append("</form>")
' Sección para el resultado del comando
sb.Append("<hr><h2>Resultado del Comando: '" & Command & "'</h2>")
sb.Append("<div class='output'>")
' 3. --- Lógica de TUS COMANDOS (modificada para usar sb.Append) ---
If Command = "reload" Then
Private estaDB As String = ""
For i = 0 To Main.listaDeCP.Size - 1
Main.Connectors.Get(Main.listaDeCP.get(i)).As(RDCConnector).Initialize(Main.listaDeCP.get(i))
If Main.listaDeCP.get(i) <> "DB1" Then estaDB = "." & Main.listaDeCP.get(i) Else estaDB = ""
sb.Append($"Recargando config${estaDB}.properties ($DateTime{DateTime.Now})<br/>"$)
sb.Append($"Queries en config.properties: <b>${Main.Connectors.Get(Main.listaDeCP.get(i)).As(RDCConnector).commands.Size}</b><br/>"$)
sb.Append($"<b>JdbcUrl:</b> ${Main.Connectors.Get(Main.listaDeCP.get(i)).As(RDCConnector).config.Get("JdbcUrl")}</b><br/>"$)
sb.Append($"<b>User:</b> ${Main.Connectors.Get(Main.listaDeCP.get(i)).As(RDCConnector).config.Get("User")}</b><br/>"$)
sb.Append($"<b>ServerPort:</b> ${Main.srvr.Port}</b><br/><br/>"$)
Next
else If Command = "stop" Then
' Tu código para "stop"
else If Command = "rsx" Then
Log($"Ejecutamos ${File.DirApp}\start.bat"$)
sb.Append($"Ejecutamos ${File.DirApp}\start.bat"$)
Public shl As Shell
shl.Initialize("shl","cmd",Array("/c",File.DirApp & "\start.bat " & Main.srvr.Port))
shl.WorkingDirectory = File.DirApp
shl.Run(-1)
else If Command = "rpm2" Then
Log($"Ejecutamos ${File.DirApp}\reiniciaProcesoPM2.bat"$)
sb.Append($"Ejecutamos ${File.DirApp}\reiniciaProcesoPM2.bat"$)
Public shl As Shell
shl.Initialize("shl","cmd",Array("/c",File.DirApp & "\reiniciaProcesoPM2.bat " & Main.srvr.Port))
shl.WorkingDirectory = File.DirApp
shl.Run(-1)
else If Command = "reviveBow" Then
Log($"Ejecutamos ${File.DirApp}\reiniciaProcesoBow.bat"$)
sb.Append($"Ejecutamos ${File.DirApp}\reiniciaProcesoBow.bat<br><br>"$)
sb.Append($"!!!BOW REINICIANDO!!!"$)
Public shl As Shell
shl.Initialize("shl","cmd",Array("/c",File.DirApp & "\reiniciaProcesoBow.bat " & Main.srvr.Port))
shl.WorkingDirectory = File.DirApp
shl.Run(-1)
else if Command = "totalrequests" Then
If GlobalParameters.mpTotalRequests.IsInitialized Then
j.Initialize(GlobalParameters.mpTotalRequests)
sb.Append(j.ToString)
End If
else if Command = "ping" Then
sb.Append($"Pong ($DateTime{DateTime.Now})"$)
End If
'...(aquí continuaría el resto de tus Else If)...
' 4. --- Cerramos la página y la enviamos TODA JUNTA ---
sb.Append("</div>") ' Cierre de div.output
sb.Append("<p class='logout'><a href='/logout'>Cerrar Sesión</a></p>")
sb.Append("</body></html>")
resp.Write(sb.ToString) ' Se envía toda la página de una vez
' Lógica final de logs (se mantiene igual)
If GlobalParameters.mpLogs.IsInitialized Then GlobalParameters.mpLogs.Put(Command, "Manager : " & Command & " - Time : " & DateTime.Time(DateTime.Now))
End Sub

View File

@@ -0,0 +1,69 @@
B4J=true
Group=Default Group
ModulesStructureVersion=1
Type=StaticCode
Version=10.3
@EndOfDesignText@
' Archivo: ParameterValidationUtils.bas
' Módulo de utilidad: ParameterValidationUtils
' Centraliza la lógica de validación y ajuste de parámetros SQL.
' Ahora soporta recorte de parámetros excesivos.
Sub Process_Globals
' El Type ParameterValidationResult está declarado en Main.bas, no se declara aquí.
End Sub
' Valida y ajusta la lista de parámetros para la ejecución SQL, aplicando la lógica de tolerancia.
' Retorna un ParameterValidationResult indicando éxito/error y los parámetros a usar.
Public Sub ValidateAndAdjustParameters (CommandName As String, DBKey As String, sqlCommand As String, receivedParams As List, IsToleranceEnabled As Boolean) As ParameterValidationResult
Dim res As ParameterValidationResult
res.Initialize
res.Success = True ' Asumimos éxito inicialmente
Log(">>>> IsToleranceEnabled: " & IsToleranceEnabled)
' Aseguramos que receivedParams esté inicializada, incluso si está vacía o Null
If receivedParams = Null Or receivedParams.IsInitialized = False Then
receivedParams.Initialize ' Inicializa una lista vacía si es Null o no inicializada.
End If
' Contar cuántos '?' hay en la sentencia SQL para saber cuántos parámetros se esperan.
Dim expectedParams As Int = sqlCommand.Length - sqlCommand.Replace("?", "").Length
Dim receivedParamsSize As Int = receivedParams.Size
If receivedParamsSize < expectedParams Then
' Caso 1: Se recibieron MENOS parámetros de los esperados. Esto es un error.
res.Success = False
res.ErrorMessage = $"ERROR: Número de parámetros insuficiente para "${CommandName}" (DB: ${DBKey}). Se esperaban ${expectedParams} y se recibieron ${receivedParamsSize}."$
Log(res.ErrorMessage)
Main.LogServerError("ERROR", "ParameterValidationUtils.ValidateAndAdjustParameters", res.ErrorMessage, DBKey, CommandName, Null) ' <-- Nuevo Log
Return res
Else If receivedParamsSize > expectedParams Then
' Caso 2: Se recibieron MÁS parámetros de los esperados.
If IsToleranceEnabled Then ' Solo recortamos si la tolerancia está habilitada
Dim adjustedParams As List
adjustedParams.Initialize
For i = 0 To expectedParams - 1
adjustedParams.Add(receivedParams.Get(i))
Next
res.ParamsToExecute = adjustedParams
res.Success = True
Dim WarningMsg As String = $"ADVERTENCIA: Se recibieron más parámetros de los esperados para "${CommandName}" (DB: ${DBKey}). Se esperaban ${expectedParams} y se recibieron ${receivedParamsSize}. Se ajustó la lista de parámetros a ${expectedParams} elementos."$
Log(WarningMsg)
Main.LogServerError("ADVERTENCIA", "ParameterValidationUtils.ValidateAndAdjustParameters", WarningMsg, DBKey, CommandName, Null) ' <-- Nuevo Log [6]
Else
' Si la tolerancia NO está habilitada, esto es un error crítico.
res.Success = False
res.ErrorMessage = $"ERROR: Número de parámetros excesivo para "${CommandName}" (DB: ${DBKey}). Se esperaban ${expectedParams} y se recibieron ${receivedParamsSize}. La tolerancia a parámetros de más está DESHABILITADA."$
Log(res.ErrorMessage)
Main.LogServerError("ERROR", "ParameterValidationUtils.ValidateAndAdjustParameters", res.ErrorMessage, DBKey, CommandName, Null)
Return res
End If
Else
' Caso 3: Se recibieron el número EXACTO de parámetros. Todo bien.
res.ParamsToExecute = receivedParams ' Usamos la lista original tal cual.
res.Success = True ' Confirmamos éxito.
End If
Return res
End Sub

View File

@@ -31,6 +31,9 @@ Sub Class_Globals
' Almacena la configuración completa (DriverClass, JdbcUrl, User, Password, InitialPoolSize, etc.) ' Almacena la configuración completa (DriverClass, JdbcUrl, User, Password, InitialPoolSize, etc.)
' cargada de su respectivo archivo .properties. ' cargada de su respectivo archivo .properties.
Public config As Map Public config As Map
' Indica si la tolerancia a parámetros de más está activa.
Public IsParameterToleranceEnabled As Boolean
End Sub End Sub
' Subrutina de inicialización para el conector de una base de datos específica. ' Subrutina de inicialización para el conector de una base de datos específica.
@@ -45,6 +48,16 @@ Public Sub Initialize(DB As String)
' para que la configuración cargada del archivo sea persistente para esta instancia del conector. ' para que la configuración cargada del archivo sea persistente para esta instancia del conector.
config = LoadConfigMap(DB) config = LoadConfigMap(DB)
' Leer la configuración de tolerancia de parámetros
Dim toleranceSetting As Int = config.GetDefault("parameterTolerance", 0).As(Int) ' Por defecto, 0 (estricto)
IsParameterToleranceEnabled = (toleranceSetting = 1) ' La tolerancia se habilita si el valor es 1
If IsParameterToleranceEnabled Then
Log($"RDCConnector.Initialize para ${DB}: Tolerancia a parámetros de más, HABILITADA."$)
Else
Log($"RDCConnector.Initialize para ${DB}: Tolerancia a parámetros de más, DESHABILITADA (modo estricto)."$)
End If
' Bloque Try-Catch para la inicialización y configuración del pool. ' Bloque Try-Catch para la inicialización y configuración del pool.
' Esto es esencial para capturar cualquier error crítico que impida la conexión inicial a la base de datos. ' Esto es esencial para capturar cualquier error crítico que impida la conexión inicial a la base de datos.
Try Try
@@ -78,7 +91,7 @@ Public Sub Initialize(DB As String)
' LÍNEAS CRÍTICAS PARA FORZAR UN COMPORTAMIENTO NO SILENCIOSO DE C3P0: ' LÍNEAS CRÍTICAS PARA FORZAR UN COMPORTAMIENTO NO SILENCIOSO DE C3P0:
' Por defecto, C3P0 puede reintentar muchas veces y no lanzar una excepción si las conexiones iniciales fallan. ' Por defecto, C3P0 puede reintentar muchas veces y no lanzar una excepción si las conexiones iniciales fallan.
' Estas líneas fuerzan a C3P0 a ser estricto y reportar errores de inmediato. ' Estas líneas fuerzan a C3P0 a ser estricto y reportar errores de inmediato.
jo.RunMethod("setAcquireRetryAttempts", Array As Object(1)) ' Limita los intentos iniciales de adquisición a 1. jo.RunMethod("setAcquireRetryAttempts", Array As Object(2)) ' Limita los intentos iniciales de adquisición a 1.
jo.RunMethod("setBreakAfterAcquireFailure", Array As Object(True)) ' ¡Forza a C3P0 a lanzar una excepción si falla al adquirir conexiones! jo.RunMethod("setBreakAfterAcquireFailure", Array As Object(True)) ' ¡Forza a C3P0 a lanzar una excepción si falla al adquirir conexiones!
' PASO 4: Forzar la creación de conexiones iniciales y verificar el estado. ' PASO 4: Forzar la creación de conexiones iniciales y verificar el estado.
@@ -138,7 +151,9 @@ Public Sub Initialize(DB As String)
Catch Catch
' Si ocurre un error durante la inicialización del pool o al forzar la conexión, ' Si ocurre un error durante la inicialización del pool o al forzar la conexión,
' este Log es CRÍTICO para el diagnóstico, especialmente en un entorno de producción. ' este Log es CRÍTICO para el diagnóstico, especialmente en un entorno de producción.
Log($"RDCConnector.Initialize para ${DB}: ERROR CRÍTICO al inicializar/forzar conexión: ${LastException.Message}"$) Dim ErrorMsg As String = $"RDCConnector.Initialize para ${DB}: ERROR CRÍTICO al inicializar/forzar conexión: ${LastException.Message}"$
Log(ErrorMsg)
Main.LogServerError("ERROR", "RDCConnector.Initialize", ErrorMsg, DB, Null, Null)
End Try End Try
' Configuración de depuración de queries. Se activa automáticamente si el proyecto se ejecuta en modo DEBUG. ' Configuración de depuración de queries. Se activa automáticamente si el proyecto se ejecuta en modo DEBUG.
@@ -178,7 +193,9 @@ Public Sub GetCommand(DB As String, Key As String) As String
' Obtiene los comandos de la DB específica del mapa global Main.commandsMap. ' Obtiene los comandos de la DB específica del mapa global Main.commandsMap.
commands = Main.commandsMap.Get(DB).As(Map) commands = Main.commandsMap.Get(DB).As(Map)
If commands.ContainsKey("sql." & Key) = False Then If commands.ContainsKey("sql." & Key) = False Then
Log($"RDCConnector.GetCommand: *** Comando no encontrado: '${Key}' para DB: '${DB}' ***"$) ' Log importante si un comando no se encuentra. Dim ErrorMsg As String = $"RDCConnector.GetCommand: *** Comando no encontrado: '${Key}' para DB: '${DB}' ***"$
Log(ErrorMsg)
Main.LogServerError("ERROR", "RDCConnector.GetCommand", ErrorMsg, DB, Key, Null) ' Log importante si un comando no se encuentra.
End If End If
Return commands.Get("sql." & Key) ' Retorna la sentencia SQL. Return commands.Get("sql." & Key) ' Retorna la sentencia SQL.
End Sub End Sub
@@ -285,12 +302,16 @@ Public Sub GetPoolStats As Map
Catch Catch
' Si ocurre un error al obtener las estadísticas, se registra y se añade un mensaje de error al mapa. ' Si ocurre un error al obtener las estadísticas, se registra y se añade un mensaje de error al mapa.
Log("RDCConnector.GetPoolStats: ERROR CRÍTICO al obtener estadísticas del pool: " & LastException.Message) Dim ErrorMsg As String = "RDCConnector.GetPoolStats: ERROR CRÍTICO al obtener estadísticas del pool: " & LastException.Message
Log(ErrorMsg)
Main.LogServerError("ERROR", "RDCConnector.GetPoolStats", ErrorMsg, "Todas", Null, Null) ' <-- Nuevo Log
stats.Put("Error", LastException.Message) stats.Put("Error", LastException.Message)
End Try End Try
Else Else
' Si el pool no está inicializado, se registra una advertencia y se devuelve un mapa con un error. ' Si el pool no está inicializado, se registra una advertencia y se devuelve un mapa con un error.
Log("RDCConnector.GetPoolStats: ADVERTENCIA: Pool NO está inicializado. Retornando mapa con error.") Dim WarningMsg As String = "RDCConnector.GetPoolStats: ADVERTENCIA: Pool NO está inicializado. Retornando mapa con error."
Log(WarningMsg)
Main.LogServerError("ADVERTENCIA", "RDCConnector.GetPoolStats", WarningMsg, "Todas", Null, Null) ' <-- Nuevo Log
stats.Put("Error", "Pool de conexiones no inicializado para esta DB.") stats.Put("Error", "Pool de conexiones no inicializado para esta DB.")
End If End If

View File

@@ -22,7 +22,7 @@ Sub Handle(req As ServletRequest, resp As ServletResponse)
' Dim con As SQL = Main.rdcConnectorDB1.GetConnection("") ' Dim con As SQL = Main.rdcConnectorDB1.GetConnection("")
Dim con As SQL = Main.Connectors.Get("DB1").As(RDCConnector).GetConnection("") Dim con As SQL = Main.Connectors.Get("DB1").As(RDCConnector).GetConnection("")
resp.Write("Connection successful.</br></br>") resp.Write("Connection successful.</br></br>")
Private estaDB As String = "" Dim estaDB As String = ""
Log(Main.listaDeCP) Log(Main.listaDeCP)
For i = 0 To Main.listaDeCP.Size - 1 For i = 0 To Main.listaDeCP.Size - 1
If Main.listaDeCP.get(i) <> "" Then estaDB = "." & Main.listaDeCP.get(i) If Main.listaDeCP.get(i) <> "" Then estaDB = "." & Main.listaDeCP.get(i)

View File

@@ -32,9 +32,10 @@ Library8=jsql
Library9=bcrypt Library9=bcrypt
Module1=Cambios Module1=Cambios
Module10=Manager Module10=Manager
Module11=ping Module11=ParameterValidationUtils
Module12=RDCConnector Module12=ping
Module13=TestHandler Module13=RDCConnector
Module14=TestHandler
Module2=ChangePassHandler Module2=ChangePassHandler
Module3=DBHandlerB4X Module3=DBHandlerB4X
Module4=DBHandlerJSON Module4=DBHandlerJSON
@@ -45,7 +46,7 @@ Module8=LoginHandler
Module9=LogoutHandler Module9=LogoutHandler
NumberOfFiles=10 NumberOfFiles=10
NumberOfLibraries=9 NumberOfLibraries=9
NumberOfModules=13 NumberOfModules=14
Version=10.3 Version=10.3
@EndOfDesignText@ @EndOfDesignText@
'Non-UI application (console / server application) 'Non-UI application (console / server application)
@@ -53,7 +54,7 @@ Version=10.3
#Region Project Attributes #Region Project Attributes
#CommandLineArgs: #CommandLineArgs:
#MergeLibraries: True #MergeLibraries: True
' VERSION 5.09.15 ' VERSION 5.09.16
'########################################################################################################### '###########################################################################################################
'###################### PULL ############################################################# '###################### PULL #############################################################
'Ctrl + click ide://run?file=%WINDIR%\System32\cmd.exe&Args=/c&Args=git&Args=pull 'Ctrl + click ide://run?file=%WINDIR%\System32\cmd.exe&Args=/c&Args=git&Args=pull
@@ -107,11 +108,15 @@ Sub Process_Globals
' Objeto de bloqueo (ReentrantLock) para proteger el mapa Main.Connectors durante operaciones de recarga (Hot-Swap). ' Objeto de bloqueo (ReentrantLock) para proteger el mapa Main.Connectors durante operaciones de recarga (Hot-Swap).
Public MainConnectorsLock As JavaObject Public MainConnectorsLock As JavaObject
' Mapa para contar las peticiones activas por cada base de datos. Es thread-safe.
Public ActiveRequestsCountByDB As Map
' Timer para ejecutar tareas periódicas, como la limpieza de logs. ' Timer para ejecutar tareas periódicas, como la limpieza de logs.
Public timerLogs As Timer Public timerLogs As Timer
' Tipo para encapsular el resultado de la validación de parámetros.
Type ParameterValidationResult ( _
Success As Boolean, _
ErrorMessage As String, _
ParamsToExecute As List _ ' La lista de parámetros final a usar en la ejecución SQL
)
End Sub End Sub
Sub AppStart (Args() As String) Sub AppStart (Args() As String)
@@ -149,12 +154,20 @@ Sub AppStart (Args() As String)
' === 4. INICIALIZACIÓN DEL CONECTOR PARA LA BASE DE DATOS PRINCIPAL (DB1) === ' === 4. INICIALIZACIÓN DEL CONECTOR PARA LA BASE DE DATOS PRINCIPAL (DB1) ===
' DB1 siempre usa el archivo 'config.properties' por defecto. ' DB1 siempre usa el archivo 'config.properties' por defecto.
Try
Dim con1 As RDCConnector ' Declara una variable específica y única para el conector de DB1. Dim con1 As RDCConnector ' Declara una variable específica y única para el conector de DB1.
con1.Initialize("DB1") ' Inicializa la instancia del conector para "DB1". con1.Initialize("DB1") ' Inicializa la instancia del conector para "DB1".
Connectors.Put("DB1", con1) ' Asocia el identificador "DB1" con su instancia de RDCConnector. Connectors.Put("DB1", con1) ' Asocia el identificador "DB1" con su instancia de RDCConnector.
srvr.Port = con1.serverPort ' El puerto del servidor HTTP se obtiene del config.properties de DB1. srvr.Port = con1.serverPort ' El puerto del servidor HTTP se obtiene del config.properties de DB1.
listaDeCP.Add("DB1") ' Añade "DB1" a la lista de bases de datos gestionadas. listaDeCP.Add("DB1") ' Añade "DB1" a la lista de bases de datos gestionadas.
Log($"Main.AppStart: Conector 'DB1' inicializado exitosamente en puerto: ${srvr.Port}"$) Log($"Main.AppStart: Conector 'DB1' inicializado exitosamente en puerto: ${srvr.Port}"$)
Catch
Dim ErrorMsg As String = $"Main.AppStart: ERROR CRÍTICO al inicializar conector 'DB1': ${LastException.Message}"$
Log(ErrorMsg)
LogServerError("ERROR", "Main.AppStart", ErrorMsg, "DB1", Null, Null)
' Si DB1 falla, el servidor no puede arrancar correctamente.
ExitApplication
End Try
' === 5. DETECCIÓN E INICIALIZACIÓN DE BASES DE DATOS ADICIONALES (DB2, DB3, DB4) === ' === 5. DETECCIÓN E INICIALIZACIÓN DE BASES DE DATOS ADICIONALES (DB2, DB3, DB4) ===
' El servidor busca archivos de configuración adicionales (ej. config.DB2.properties) ' El servidor busca archivos de configuración adicionales (ej. config.DB2.properties)
@@ -164,27 +177,48 @@ Sub AppStart (Args() As String)
For i = 0 To cpFiles.Size - 1 For i = 0 To cpFiles.Size - 1
' Procesa 'config.DB2.properties' ' Procesa 'config.DB2.properties'
If cpFiles.Get(i) = "config.DB2.properties" Then If cpFiles.Get(i) = "config.DB2.properties" Then
Dim con2 As RDCConnector ' Declara una variable específica y única para el conector de DB2. Try
con2.Initialize("DB2") ' Inicializa la instancia del conector para "DB2". Dim con2 As RDCConnector
Connectors.Put("DB2", con2) ' Asocia "DB2" con su instancia de RDCConnector. con2.Initialize("DB2")
listaDeCP.Add("DB2") ' Añade "DB2" a la lista de bases de datos. Connectors.Put("DB2", con2)
listaDeCP.Add("DB2")
Log("Main.AppStart: Conector 'DB2' inicializado exitosamente.") Log("Main.AppStart: Conector 'DB2' inicializado exitosamente.")
Catch
Dim ErrorMsg As String = $"Main.AppStart: ERROR al inicializar conector 'DB2': ${LastException.Message}"$
Log(ErrorMsg)
LogServerError("ERROR", "Main.AppStart", ErrorMsg, "DB2", Null, Null)
' Si un conector secundario falla, el servidor puede continuar, pero es importante saberlo.
End Try
End If End If
' Procesa 'config.DB3.properties' ' Procesa 'config.DB3.properties'
If cpFiles.Get(i) = "config.DB3.properties" Then If cpFiles.Get(i) = "config.DB3.properties" Then
Dim con3 As RDCConnector ' Declara una variable específica y única para el conector de DB3. Try
con3.Initialize("DB3") ' Inicializa la instancia del conector para "DB3". Dim con3 As RDCConnector
Connectors.Put("DB3", con3) ' Asocia "DB3" con su instancia de RDCConnector. con3.Initialize("DB3")
listaDeCP.Add("DB3") ' Añade "DB3" a la lista de bases de datos. Connectors.Put("DB3", con3)
listaDeCP.Add("DB3")
Log("Main.AppStart: Conector 'DB3' inicializado exitosamente.") Log("Main.AppStart: Conector 'DB3' inicializado exitosamente.")
Catch
Dim ErrorMsg As String = $"Main.AppStart: ERROR al inicializar conector 'DB3': ${LastException.Message}"$
Log(ErrorMsg)
LogServerError("ERROR", "Main.AppStart", ErrorMsg, "DB3", Null, Null)
' Si un conector secundario falla, el servidor puede continuar, pero es importante saberlo.
End Try
End If End If
' Procesa 'config.DB4.properties' ' Procesa 'config.DB4.properties'
If cpFiles.Get(i) = "config.DB4.properties" Then If cpFiles.Get(i) = "config.DB4.properties" Then
Dim con4 As RDCConnector ' Declara una variable específica y única para el conector de DB4. Try
con4.Initialize("DB4") ' Inicializa la instancia del conector para "DB4". Dim con4 As RDCConnector
Connectors.Put("DB4", con4) ' Asocia "DB4" con su instancia de RDCConnector. con4.Initialize("DB4")
listaDeCP.Add("DB4") ' Añade "DB4" a la lista de bases de datos. Connectors.Put("DB4", con4)
listaDeCP.Add("DB4")
Log("Main.AppStart: Conector 'DB4' inicializado exitosamente.") Log("Main.AppStart: Conector 'DB4' inicializado exitosamente.")
Catch
Dim ErrorMsg As String = $"Main.AppStart: ERROR al inicializar conector 'DB4': ${LastException.Message}"$
Log(ErrorMsg)
LogServerError("ERROR", "Main.AppStart", ErrorMsg, "DB4", Null, Null)
' Si un conector secundario falla, el servidor puede continuar, pero es importante saberlo.
End Try
End If End If
Next Next
Else Else
@@ -261,6 +295,15 @@ Sub InitializeSQLiteDatabase
' Inserta el usuario por defecto en la tabla 'users'. ' Inserta el usuario por defecto en la tabla 'users'.
SQL1.ExecNonQuery2("INSERT INTO users (username, password_hash) VALUES (?, ?)", Array As Object(defaultUser, hashedPass)) SQL1.ExecNonQuery2("INSERT INTO users (username, password_hash) VALUES (?, ?)", Array As Object(defaultUser, hashedPass))
Log($"Usuario por defecto creado -> user: ${defaultUser}, pass: ${defaultPass}"$) Log($"Usuario por defecto creado -> user: ${defaultUser}, pass: ${defaultPass}"$)
Dim createQueryLogsTable As String = "CREATE TABLE query_logs (id INTEGER PRIMARY KEY AUTOINCREMENT, query_name TEXT, duration_ms INTEGER, timestamp INTEGER, db_key TEXT, client_ip TEXT, busy_connections INTEGER, handler_active_requests INTEGER)"
SQL1.ExecNonQuery(createQueryLogsTable)
' >>> INICIO: Creación de la tabla errores con columnas de error/advertencia <<<
Log("Creando tabla 'errores' para registrar eventos.")
Dim createErrorsTable As String = "CREATE TABLE errores (id INTEGER PRIMARY KEY AUTOINCREMENT, timestamp INTEGER, type TEXT, source TEXT, message TEXT, db_key TEXT, command_name TEXT, client_ip TEXT)"
SQL1.ExecNonQuery(createErrorsTable)
' >>> FIN: Creación de la tabla errores <<<
Else Else
' Si el archivo de la base de datos ya existe, simplemente se abre. ' Si el archivo de la base de datos ya existe, simplemente se abre.
SQL1.InitializeSQLite(File.DirApp, dbFileName, True) SQL1.InitializeSQLite(File.DirApp, dbFileName, True)
@@ -311,6 +354,19 @@ Sub InitializeSQLiteDatabase
Log("Añadiendo columna 'handler_active_requests' a query_logs.") Log("Añadiendo columna 'handler_active_requests' a query_logs.")
SQL1.ExecNonQuery("ALTER TABLE query_logs ADD COLUMN handler_active_requests INTEGER DEFAULT 0") SQL1.ExecNonQuery("ALTER TABLE query_logs ADD COLUMN handler_active_requests INTEGER DEFAULT 0")
End If End If
' >>> INICIO: Lógica de migración para 'errores' si la DB ya existía <<<
Log("Verificando y migrando tabla 'errores' si es necesario.")
If SQL1.ExecQuerySingleResult("SELECT name FROM sqlite_master WHERE type='table' AND name='errores'") = Null Then
Log("Tabla 'errores' no encontrada, creándola.")
Dim createErrorsTable As String = "CREATE TABLE errores (id INTEGER PRIMARY KEY AUTOINCREMENT, timestamp INTEGER, type TEXT, source TEXT, message TEXT, db_key TEXT, command_name TEXT, client_ip TEXT)"
SQL1.ExecNonQuery(createErrorsTable)
Else
' Si la tabla ya existe, podrías añadir lógica para ALTER TABLE si se añaden nuevas columnas en el futuro.
' Por ahora, asumimos que la estructura inicial es suficiente.
Log("Tabla 'errores' ya existe.")
End If
' >>> FIN: Lógica de migración para 'errores' <<<
End If End If
' >>> FIN: Lógica de migración (ALTER TABLE) <<< ' >>> FIN: Lógica de migración (ALTER TABLE) <<<
End If End If
@@ -330,13 +386,33 @@ Public Sub LogQueryPerformance(QueryName As String, DurationMs As Long, DbKey As
End Try End Try
End Sub End Sub
' Subrutina para registrar errores y advertencias en la tabla 'errores'.
' Type: "ERROR" o "ADVERTENCIA"
' Source: Módulo.Subrutina donde ocurrió el evento (ej. "DBHandlerJSON.Handle")
' Message: El mensaje descriptivo del error/advertencia.
' DBKey: La clave de la base de datos involucrada (ej. "DB1", "DB2"), Null si no aplica.
' CommandName: El nombre del comando SQL (ej. "select_user"), Null si no aplica.
' ClientIp: La dirección IP del cliente que originó la petición, Null si no aplica.
Public Sub LogServerError(Type0 As String, Source As String, Message As String, DBKey As String, CommandName As String, ClientIp As String)
Try
SQL1.ExecNonQuery2("INSERT INTO errores (timestamp, type, source, message, db_key, command_name, client_ip) VALUES (?, ?, ?, ?, ?, ?, ?)", _
Array As Object(DateTime.Now, Type0, Source, Message, DBKey, CommandName, ClientIp))
Catch
Log("ERROR CRÍTICO: Fallo al guardar el log de error/advertencia en SQLite (Main.LogServerError): " & LastException.Message)
' En este punto, no podemos hacer mucho más que loggear el fallo en la consola,
' para evitar un bucle infinito de errores de logging.
End Try
End Sub
' Subrutina de evento para el Timer 'timerLogs'. ' Subrutina de evento para el Timer 'timerLogs'.
' Se ejecuta periódicamente (cada 10 minutos) para limpiar la tabla de logs. ' Se ejecuta periódicamente (cada 10 minutos) para limpiar la tabla de logs.
Sub TimerLogs_Tick Sub TimerLogs_Tick
Try Try
borraArribaDe15000Logs ' Llama a la función para limpiar los logs. borraArribaDe15000Logs ' Llama a la función para limpiar los logs.
Catch Catch
Log("ERROR en TimerLogs_Tick al intentar borrar logs: " & LastException.Message) Dim ErrorMsg As String = "ERROR en TimerLogs_Tick al intentar borrar logs: " & LastException.Message
Log(ErrorMsg)
LogServerError("ERROR", "Main.TimerLogs_Tick", ErrorMsg, Null, "log_cleanup", Null) ' <-- Nuevo Log
End Try End Try
End Sub End Sub

View File

@@ -4,6 +4,7 @@ ModuleBookmarks10=
ModuleBookmarks11= ModuleBookmarks11=
ModuleBookmarks12= ModuleBookmarks12=
ModuleBookmarks13= ModuleBookmarks13=
ModuleBookmarks14=
ModuleBookmarks2= ModuleBookmarks2=
ModuleBookmarks3= ModuleBookmarks3=
ModuleBookmarks4= ModuleBookmarks4=
@@ -18,6 +19,7 @@ ModuleBreakpoints10=
ModuleBreakpoints11= ModuleBreakpoints11=
ModuleBreakpoints12= ModuleBreakpoints12=
ModuleBreakpoints13= ModuleBreakpoints13=
ModuleBreakpoints14=
ModuleBreakpoints2= ModuleBreakpoints2=
ModuleBreakpoints3= ModuleBreakpoints3=
ModuleBreakpoints4= ModuleBreakpoints4=
@@ -32,14 +34,15 @@ ModuleClosedNodes10=
ModuleClosedNodes11= ModuleClosedNodes11=
ModuleClosedNodes12= ModuleClosedNodes12=
ModuleClosedNodes13= ModuleClosedNodes13=
ModuleClosedNodes14=
ModuleClosedNodes2= ModuleClosedNodes2=
ModuleClosedNodes3= ModuleClosedNodes3=9,10,11,12,13,14,15,16
ModuleClosedNodes4= ModuleClosedNodes4=
ModuleClosedNodes5= ModuleClosedNodes5=
ModuleClosedNodes6= ModuleClosedNodes6=
ModuleClosedNodes7= ModuleClosedNodes7=
ModuleClosedNodes8= ModuleClosedNodes8=
ModuleClosedNodes9= ModuleClosedNodes9=
NavigationStack=Main,Process_Globals,48,0,Manager,Class_Globals,9,0,Manager,Initialize,14,0,Manager,Handle0,463,0,Manager,Handle,157,4,faviconHandler,Handle,31,0,Main,AppStart,76,1,Main,TimerLogs_Tick,291,0,Main,LogQueryPerformance,281,0,Cambios,Process_Globals,41,0 NavigationStack=DBHandlerB4X,ExecuteBatch2,386,0,DBHandlerB4X,ExecuteBatch,449,0,DBHandlerB4X,ExecuteQuery,506,0,DBHandlerJSON,Handle,148,0,Main,Process_Globals,59,0,Manager,Handle,375,0,TestHandler,Handle,25,1,ChangePassHandler,Handle,17,0,Main,AppStart,157,0,Cambios,Process_Globals,13,6
SelectedBuild=0 SelectedBuild=0
VisibleModules=3,4,12,1,2,5,10,6 VisibleModules=3,4,13,1,10,11,14,2