mirror of
https://github.com/KeymonSoft/jRDC-Multi.git
synced 2026-04-17 21:06:24 +00:00
- 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:
318
Cambios.bas
318
Cambios.bas
@@ -10,174 +10,154 @@ Version=10.3
|
||||
|
||||
Sub Process_Globals
|
||||
|
||||
'- VERSION X.XX.XX (cambios a implementar)
|
||||
'- Agregar que se puedan usar cualquier cantidad de archivos config.properties
|
||||
'- Agregar que se pueda recargar solo un archivo de configuracion o todos a la vez.
|
||||
'- 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".
|
||||
'- Agregar una forma de probar con carga el servidor
|
||||
'- Agregar la opcion de "Queries lentos"
|
||||
|
||||
'- 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,
|
||||
' 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 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.
|
||||
'
|
||||
' 6. **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`.
|
||||
' * 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.
|
||||
|
||||
'- VERSION 5.09.14 (Ahora consolidado en 5.09.15)
|
||||
' -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.
|
||||
' -Problemas Identificados y Resueltos:
|
||||
' -1. **Métricas de `BusyConnections` y `TotalConnections` inconsistentes o siempre en `0` en el `Manager` y `query_logs`:**
|
||||
' * **Problema**: Anteriormente, la métrica `busy_connections` en `query_logs` a menudo reportaba `0` o no reflejaba el estado real. De manera similar, el panel de `Manager?command=totalcon` consistentemente mostraba `BusyConnections: 0` y `TotalConnections` estancadas en `InitialPoolSize`, a pesar de que Oracle sí reportaba conexiones activas. Esto generaba confusión sobre el uso real y la expansión del pool.
|
||||
' * **Solución**: Se modificó la lógica en los *handlers* (`DBHandlerJSON.bas` y `DBHandlerB4X.bas`) para capturar la métrica `BusyConnections` directamente del pool de C3P0 **inmediatamente después de que el *handler* adquiere una conexión** (`con = Connector.GetConnection(finalDbKey)`). Este valor se pasa explícitamente a la subrutina `Main.LogQueryPerformance` para su registro en `query_logs` y para ser consumido por `Manager.bas` a través de `RDCConnector.GetPoolStats`. Esto garantiza que el valor registrado y reportado refleje con precisión el número de conexiones activas en el instante de su adquisición. Pruebas exhaustivas confirmaron que C3P0 sí reporta conexiones ocupadas y sí expande `TotalConnections` hasta `MaxPoolSize` cuando la demanda lo exige.
|
||||
' -2. **Contador `handler_active_requests` no decrementaba correctamente:**
|
||||
' * **Problema**: El contador de peticiones activas por base de datos (`GlobalParameters.ActiveRequestsCountByDB`) no mostraba un decremento consistente, resultando en un conteo que solo aumentaba o mostraba valores erráticos en los logs.
|
||||
' * **Solución**:
|
||||
' * Se aseguró la declaración `Public ActiveRequestsCountByDB As Map` en `GlobalParameters.bas`.
|
||||
' * Se garantizó su inicialización como un `srvr.CreateThreadSafeMap` en `Main.AppStart` para un manejo concurrente seguro de los contadores.
|
||||
' * En `DBHandlerJSON.bas`, la `dbKey` (obtenida del parámetro `dbx` del JSON) ahora se resuelve *antes* de incrementar el contador, asegurando que el incremento y el decremento se apliquen siempre a la misma clave de base de datos correcta.
|
||||
' * Se implementó una coerción explícita a `Int` (`.As(Int)`) para todas las operaciones de lectura y escritura (`GetDefault`, `Put`) en `GlobalParameters.ActiveRequestsCountByDB`, resolviendo problemas de tipo que causaban inconsistencias y el fallo en el decremento.
|
||||
' * La lógica de decremento en `Private Sub CleanupAndLog` (presente en ambos *handlers*) se hizo más robusta, verificando que el contador sea mayor que cero antes de decrementar para evitar valores negativos.
|
||||
' -Beneficios de estos Cambios:
|
||||
' * **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.
|
||||
' * **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)
|
||||
'- 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'.
|
||||
'- **Problemas Resueltos:**
|
||||
'- 1. **Falta de "Hot-Swap" en `reload`:** El comando `reload` en `Manager.bas` no permitía la recarga dinámica de las configuraciones de la base de datos (config.properties) sin necesidad de reiniciar el servidor. La implementación anterior simplemente re-inicializaba las instancias existentes de `RDCConnector` in-situ, sin liberar los recursos de los pools de conexión anteriores, lo cual era ineficiente y propenso a errores.
|
||||
'- 2. **Ausencia de un mecanismo de cierre de pools:** No existía un método `Close` en `RDCConnector.bas` que permitiera cerrar ordenadamente los `ConnectionPool` (C3P0) y liberar las conexiones a la base de datos, lo que era crítico para un "hot-swap" limpio .
|
||||
'- 3. **Incompatibilidad con `Sync`:** La palabra clave `Sync` de B4X no era reconocida por el entorno de desarrollo del usuario, impidiendo su uso para la sincronización de hilos necesaria en el "hot-swap".
|
||||
'- 4. **Ausencia de `Finally` en B4X:** La palabra clave `Finally` (común en otros lenguajes como Java para asegurar la liberación de recursos) no está disponible directamente en B4X, lo cual planteó un desafío para garantizar la liberación del `ReentrantLock` de forma segura.
|
||||
'- **Cambios Implementados:**
|
||||
'- **En `Main.bas`:**
|
||||
'- * **Declaración de `MainConnectorsLock`:** Se añadió `Public MainConnectorsLock As JavaObject` en `Sub Process_Globals` para declarar una instancia de `java.util.concurrent.locks.ReentrantLock`, que servirá como objeto de bloqueo global para proteger el mapa `Main.Connectors`.
|
||||
'- * **Inicialización de `MainConnectorsLock`:** Se inicializó `MainConnectorsLock.InitializeNewInstance("java.util.concurrent.locks.ReentrantLock", Null)` en `Sub AppStart`, asegurando que el objeto de bloqueo esté listo al inicio del servidor.
|
||||
'- **En `RDCConnector.bas`:**
|
||||
'- * **Método `Public Sub Close`:** Se añadió esta subrutina al final del módulo. Utiliza `JavaObject` para invocar `joPool.RunMethod("close", Null)` sobre la instancia subyacente de C3P0, permitiendo un cierre ordenado y la liberación de todas las conexiones del pool .
|
||||
'- **En `Manager.bas`:**
|
||||
'- * **Reemplazo completo de la lógica `If Command = "reload" Then`:**
|
||||
'- * **Creación de `newConnectors`:** Se crea un mapa temporal (`Dim newConnectors As Map`) para inicializar las **nuevas instancias** de `RDCConnector` con la configuración fresca de los archivos `.properties` .
|
||||
'- * **Preservación de `oldConnectors`:** Se almacena una referencia al mapa `Main.Connectors` actual en un nuevo mapa (`Dim oldConnectors As Map`) para tener acceso a los conectores antiguos que necesitan ser cerrados .
|
||||
'- * **Sincronización con `ReentrantLock`:** Para proteger la manipulación del mapa `Main.Connectors` (que es compartido por múltiples hilos), se utilizan `Main.MainConnectorsLock.RunMethod("lock", Null)` y `Main.MainConnectorsLock.RunMethod("unlock", Null)`. Esto asegura que el reemplazo del mapa sea atómico, es decir, que solo un hilo pueda acceder a `Main.Connectors` durante la lectura y la escritura .
|
||||
'- * **Manejo de Bloqueo Seguro sin `Finally`:** Dado que `Finally` no está disponible en B4X, se implementó un patrón con una bandera booleana (`lockAcquired`) dentro de un bloque `Try...Catch` para garantizar que `unlock()` siempre se ejecute si `lock()` fue exitoso, previniendo interbloqueos .
|
||||
'- * **Cierre explícito de `oldConnectors`:** Después de que los `newConnectors` reemplazan a los `oldConnectors`, se itera sobre el mapa `oldConnectors` y se llama a `oldRDC.Close` para cada conector, liberando sus recursos de base de datos de manera limpia .
|
||||
'- * **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 .
|
||||
'- • 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)
|
||||
'- Módulo: DBHandlerJSON.bas
|
||||
'- Descripción de Cambios: Manejo de Peticiones POST con Content-Type: application/json
|
||||
'- • Problema Identificado: La implementación anterior de DBHandlerJSON procesaba las peticiones POST esperando que el payload JSON se encontrara en el parámetro j de la URL (req.GetParameter("j")). Esto impedía la correcta lectura de peticiones POST que utilizaban Content-Type: application/json, donde el JSON se envía directamente en el cuerpo de la petición (InputStream). Como resultado, los clientes recibían un error indicando la ausencia del parámetro j .
|
||||
'- • Solución Implementada:
|
||||
'- 1. Se modificó la lógica en el método Handle para detectar explícitamente las peticiones POST con Content-Type igual a application/json.
|
||||
'- 2. En estos casos, el payload JSON ahora se lee directamente del InputStream de la petición (req.InputStream).
|
||||
'- 3. Se utilizó Bit.InputStreamToBytes(Is0) para leer el cuerpo completo de la petición a un Array de bytes, seguido de BytesToString para convertirlo en la cadena JSON.
|
||||
'- 4. Se añadió el cierre explícito del InputStream (Is0.Close) para asegurar la liberación de recursos .
|
||||
'- 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.
|
||||
'- • 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)
|
||||
' feat: Mejora la inicialización del pool de conexiones y el soporte multi-DB.
|
||||
'
|
||||
' **Problemas Resueltos:**
|
||||
'
|
||||
' 1. **Inicialización de `TotalConnections: 0` en todos los pools:** Anteriormente, el Log mostraba 0 conexiones inicializadas para todas las bases de datos (DB1, DB2, DB3, DB4) durante `AppStart`, a pesar de que los `handlers` de `DBHandlerB4X` y `DBHandlerJSON` podían conectarse más tarde bajo demanda. Esto indicaba un fallo silencioso en la creación de conexiones iniciales por parte de C3P0.
|
||||
' 2. **Configuración inconsistente de C3P0:** Parámetros críticos de C3P0 como `acquireRetryAttempts` y `breakAfterAcquireFailure` no se aplicaban correctamente al inicio, manteniendo los valores por defecto que ocultaban errores de conexión.
|
||||
' 3. **`jdbcUrl` truncado/vacío:** Se observó que la `jdbcUrl` aparecía truncada o vacía en algunos logs de C3P0, indicando un problema en la carga de la configuración.
|
||||
'
|
||||
' **Cambios Implementados:**
|
||||
'
|
||||
' **En `Main.bas`:**
|
||||
'
|
||||
' * **Declaración de conectores:** Se aseguró la declaración de variables `Dim conX As RDCConnector` separadas para cada conector (con1, con2, con3, con4) para evitar conflictos de variables y asegurar la inicialización correcta.
|
||||
'
|
||||
' **En `RDCConnector.bas`:**
|
||||
'
|
||||
' * **Corrección de shadowing de `config`:** Se modificó `LoadConfigMap(DB)` para asignar directamente a la variable de clase `config` (eliminando `Dim` local), resolviendo el problema de la `jdbcUrl` truncada y asegurando que cada `RDCConnector` use su configuración específica de manera persistente.
|
||||
' * **Reordenamiento y robustecimiento de `Initialize`:**
|
||||
' * **Carga de `config`:** Se asegura que `config` se cargue completamente en la variable de clase antes de cualquier operación del pool.
|
||||
' * **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.
|
||||
' * **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
|
||||
'- Se agregó que se puedan configurar en el config.properties los siguientes parametros:
|
||||
'- - setInitialPoolSize = 3
|
||||
'- - setMinPoolSize = 2
|
||||
'- - setMaxPoolSize = 5
|
||||
'- Se agregaron en duro a RDConnector los siguientes parametros:
|
||||
'- - setMaxIdleTime <-- Tiempo máximo de inactividad de la conexión.
|
||||
'- - setMaxConnectionAge <-- Tiempo de vida máximo de una conexión.
|
||||
'- - setCheckoutTimeout <-- Tiempo máximo de espera por una conexión.
|
||||
'- Se agregó en el config.properties, al final del "JdbcUrl" este parametro, que le indica al servidor de Oracle
|
||||
'- el nombre del cliente que se está conectando "?v$session.program=jRDC_Multi"
|
||||
'- VERSION 5.09.08
|
||||
'- Se cambio el codigo para que en lugar de esperar un mapa con los parametros del query y nombres de los parametros (par1, par2, etc) para definir el ordenamiento, ahora se espera una lista [1,"2",3], y el orden de los parametros se toma directamente del orden en el que se mandan, de la misma forma que en B4A.
|
||||
'- VERSION 5.09.04
|
||||
'- Se cambio el nombre del handler de B4X a DBHandlerB4X.
|
||||
'- Se quitaron los handlers que ya no servian.
|
||||
'- VERSION 5.09.01
|
||||
'- Se corrigieron errores en "Manager".
|
||||
'- Se cambiaron nombres de handlers.
|
||||
'- Se corrigio un error en la ruta de "www/login.html".
|
||||
'- VERSION 5.08.31
|
||||
'- Se corrigio que no avisaba cuando el query no requeria parametros y si se enviaban (en el JSONHandler)
|
||||
'- VERSION 5.08.30
|
||||
'- Se cambiaron los 4 handlers de B4A a uno solo que toma el DB de la ruta automáticamente.
|
||||
'- Se agregaron validaciones del numero de parametros y si el query no los requiere o se dan de mas o de menos, manda un error especificando eso, ya no se reciben errores directos de la base de datos, esto fue tanto para B4A como para JSON.
|
||||
'- Se modificó el Readme.md para incluir todos estos cambios.
|
||||
'- VERSION 5.08.25
|
||||
'- Se modificaron los archivos de reinicio de los servicios (servidor y Bow) y se cambio el menu del "manager" para que a seccion de "reload" incluya la liga a reinciar Bow.
|
||||
'- VERSION 5.08.02
|
||||
'- Se hizo un cambio para tratar de que las conexiones se "identifiquen" con Oracle y Jorge pueda saber que conexiones/recursos estamos ocupando
|
||||
'- VERSION 4.11.14
|
||||
'- Se agregó el parametro "setMaxPoolSize=5" para que solo genere 5 conexiones a la base de datos, antes generaba 15.
|
||||
'- Se quitaron lineas previamente comentadas.
|
||||
'- VERSION 4.11.09
|
||||
'- Commit inicial on Nov 9, 2024
|
||||
' - VERSION X.XX.XX (cambios a implementar)
|
||||
' - Agregar que se puedan usar cualquier cantidad de archivos config.properties
|
||||
' - Agregar que se pueda recargar solo un archivo de configuracion o todos a la vez.
|
||||
' - 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".
|
||||
' - Agregar una forma de probar con carga el servidor
|
||||
'
|
||||
' - 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
|
||||
'
|
||||
' 1. **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 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.
|
||||
'
|
||||
' 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`.
|
||||
' * 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.
|
||||
'
|
||||
' - VERSION 5.09.14 (Ahora consolidado en 5.09.15)
|
||||
' -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.
|
||||
' -Problemas Identificados y Resueltos:
|
||||
' -1. **Métricas de `BusyConnections` y `TotalConnections` inconsistentes o siempre en `0` en el `Manager` y `query_logs`:**
|
||||
' * **Problema**: Anteriormente, la métrica `busy_connections` en `query_logs` a menudo reportaba `0` o no reflejaba el estado real. De manera similar, el panel de `Manager?command=totalcon` consistentemente mostraba `BusyConnections: 0` y `TotalConnections` estancadas en `InitialPoolSize`, a pesar de que Oracle sí reportaba conexiones activas. Esto generaba confusión sobre el uso real y la expansión del pool.
|
||||
' * **Solución**: Se modificó la lógica en los *handlers* (`DBHandlerJSON.bas` y `DBHandlerB4X.bas`) para capturar la métrica `BusyConnections` directamente del pool de C3P0 **inmediatamente después de que el *handler* adquiere una conexión** (`con = Connector.GetConnection(finalDbKey)`). Este valor se pasa explícitamente a la subrutina `Main.LogQueryPerformance` para su registro en `query_logs` y para ser consumido por `Manager.bas` a través de `RDCConnector.GetPoolStats`. Esto garantiza que el valor registrado y reportado refleje con precisión el número de conexiones activas en el instante de su adquisición. Pruebas exhaustivas confirmaron que C3P0 sí reporta conexiones ocupadas y sí expande `TotalConnections` hasta `MaxPoolSize` cuando la demanda lo exige.
|
||||
' -2. **Contador `handler_active_requests` no decrementaba correctamente:**
|
||||
' * **Problema**: El contador de peticiones activas por base de datos (`GlobalParameters.ActiveRequestsCountByDB`) no mostraba un decremento consistente, resultando en un conteo que solo aumentaba o mostraba valores erráticos en los logs.
|
||||
' * **Solución**:
|
||||
' * Se aseguró la declaración `Public ActiveRequestsCountByDB As Map` en `GlobalParameters.bas`.
|
||||
' * Se garantizó su inicialización como un `srvr.CreateThreadSafeMap` en `Main.AppStart` para un manejo concurrente seguro de los contadores.
|
||||
' * En `DBHandlerJSON.bas`, la `dbKey` (obtenida del parámetro `dbx` del JSON) ahora se resuelve *antes* de incrementar el contador, asegurando que el incremento y el decremento se apliquen siempre a la misma clave de base de datos correcta.
|
||||
' * Se implementó una coerción explícita a `Int` (`.As(Int)`) para todas las operaciones de lectura y escritura (`GetDefault`, `Put`) en `GlobalParameters.ActiveRequestsCountByDB`, resolviendo problemas de tipo que causaban inconsistencias y el fallo en el decremento.
|
||||
' * La lógica de decremento en `Private Sub CleanupAndLog` (presente en ambos *handlers*) se hizo más robusta, verificando que el contador sea mayor que cero antes de decrementar para evitar valores negativos.
|
||||
' -Beneficios de estos Cambios:
|
||||
' * **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.
|
||||
' * **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)
|
||||
' - 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'.
|
||||
' - **Problemas Resueltos:**
|
||||
' - 1. **Falta de "Hot-Swap" en `reload`:** El comando `reload` en `Manager.bas` no permitía la recarga dinámica de las configuraciones de la base de datos (config.properties) sin necesidad de reiniciar el servidor. La implementación anterior simplemente re-inicializaba las instancias existentes de `RDCConnector` in-situ, sin liberar los recursos de los pools de conexión anteriores, lo cual era ineficiente y propenso a errores.
|
||||
' - 2. **Ausencia de un mecanismo de cierre de pools:** No existía un método `Close` en `RDCConnector.bas` que permitiera cerrar ordenadamente los `ConnectionPool` (C3P0) y liberar las conexiones a la base de datos, lo que era crítico para un "hot-swap" limpio .
|
||||
' - 3. **Incompatibilidad con `Sync`:** La palabra clave `Sync` de B4X no era reconocida por el entorno de desarrollo del usuario, impidiendo su uso para la sincronización de hilos necesaria en el "hot-swap".
|
||||
' - 4. **Ausencia de `Finally` en B4X:** La palabra clave `Finally` (común en otros lenguajes como Java para asegurar la liberación de recursos) no está disponible directamente en B4X, lo cual planteó un desafío para garantizar la liberación del `ReentrantLock` de forma segura.
|
||||
' - **Cambios Implementados:**
|
||||
' - **En `Main.bas`:**
|
||||
' - * **Declaración de `MainConnectorsLock`:** Se añadió `Public MainConnectorsLock As JavaObject` en `Sub Process_Globals` para declarar una instancia de `java.util.concurrent.locks.ReentrantLock`, que servirá como objeto de bloqueo global para proteger el mapa `Main.Connectors`.
|
||||
' - * **Inicialización de `MainConnectorsLock`:** Se inicializó `MainConnectorsLock.InitializeNewInstance("java.util.concurrent.locks.ReentrantLock", Null)` en `Sub AppStart`, asegurando que el objeto de bloqueo esté listo al inicio del servidor.
|
||||
' - **En `RDCConnector.bas`:**
|
||||
' - * **Método `Public Sub Close`:** Se añadió esta subrutina al final del módulo. Utiliza `JavaObject` para invocar `joPool.RunMethod("close", Null)` sobre la instancia subyacente de C3P0, permitiendo un cierre ordenado y la liberación de todas las conexiones del pool .
|
||||
' - **En `Manager.bas`:**
|
||||
' - * **Reemplazo completo de la lógica `If Command = "reload" Then`:**
|
||||
' - * **Creación de `newConnectors`:** Se crea un mapa temporal (`Dim newConnectors As Map`) para inicializar las **nuevas instancias** de `RDCConnector` con la configuración fresca de los archivos `.properties` .
|
||||
' - * **Preservación de `oldConnectors`:** Se almacena una referencia al mapa `Main.Connectors` actual en un nuevo mapa (`Dim oldConnectors As Map`) para tener acceso a los conectores antiguos que necesitan ser cerrados .
|
||||
' - * **Sincronización con `ReentrantLock`:** Para proteger la manipulación del mapa `Main.Connectors` (que es compartido por múltiples hilos), se utilizan `Main.MainConnectorsLock.RunMethod("lock", Null)` y `Main.MainConnectorsLock.RunMethod("unlock", Null)`. Esto asegura que el reemplazo del mapa sea atómico, es decir, que solo un hilo pueda acceder a `Main.Connectors` durante la lectura y la escritura .
|
||||
' - * **Manejo de Bloqueo Seguro sin `Finally`:** Dado que `Finally` no está disponible en B4X, se implementó un patrón con una bandera booleana (`lockAcquired`) dentro de un bloque `Try...Catch` para garantizar que `unlock()` siempre se ejecute si `lock()` fue exitoso, previniendo interbloqueos .
|
||||
' - * **Cierre explícito de `oldConnectors`:** Después de que los `newConnectors` reemplazan a los `oldConnectors`, se itera sobre el mapa `oldConnectors` y se llama a `oldRDC.Close` para cada conector, liberando sus recursos de base de datos de manera limpia .
|
||||
' - * **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 .
|
||||
' - • 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)
|
||||
' - Módulo: DBHandlerJSON.bas
|
||||
' - Descripción de Cambios: Manejo de Peticiones POST con Content-Type: application/json
|
||||
' - • Problema Identificado: La implementación anterior de DBHandlerJSON procesaba las peticiones POST esperando que el payload JSON se encontrara en el parámetro j de la URL (req.GetParameter("j")). Esto impedía la correcta lectura de peticiones POST que utilizaban Content-Type: application/json, donde el JSON se envía directamente en el cuerpo de la petición (InputStream). Como resultado, los clientes recibían un error indicando la ausencia del parámetro j .
|
||||
' - • Solución Implementada:
|
||||
' - 1. Se modificó la lógica en el método Handle para detectar explícitamente las peticiones POST con Content-Type igual a application/json.
|
||||
' - 2. En estos casos, el payload JSON ahora se lee directamente del InputStream de la petición (req.InputStream).
|
||||
' - 3. Se utilizó Bit.InputStreamToBytes(Is0) para leer el cuerpo completo de la petición a un Array de bytes, seguido de BytesToString para convertirlo en la cadena JSON.
|
||||
' - 4. Se añadió el cierre explícito del InputStream (Is0.Close) para asegurar la liberación de recursos .
|
||||
' - 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.
|
||||
' - • 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)
|
||||
' feat: Mejora la inicialización del pool de conexiones y el soporte multi-DB.
|
||||
'
|
||||
' **Problemas Resueltos:**
|
||||
'
|
||||
' 1. **Inicialización de `TotalConnections: 0` en todos los pools:** Anteriormente, el Log mostraba 0 conexiones inicializadas para todas las bases de datos (DB1, DB2, DB3, DB4) durante `AppStart`, a pesar de que los `handlers` de `DBHandlerB4X` y `DBHandlerJSON` podían conectarse más tarde bajo demanda. Esto indicaba un fallo silencioso en la creación de conexiones iniciales por parte de C3P0.
|
||||
' 2. **Configuración inconsistente de C3P0:** Parámetros críticos de C3P0 como `acquireRetryAttempts` y `breakAfterAcquireFailure` no se aplicaban correctamente al inicio, manteniendo los valores por defecto que ocultaban errores de conexión.
|
||||
' 3. **`jdbcUrl` truncado/vacío:** Se observó que la `jdbcUrl` aparecía truncada o vacía en algunos logs de C3P0, indicando un problema en la carga de la configuración.
|
||||
'
|
||||
' **Cambios Implementados:**
|
||||
'
|
||||
' **En `Main.bas`:**
|
||||
'
|
||||
' * **Declaración de conectores:** Se aseguró la declaración de variables `Dim conX As RDCConnector` separadas para cada conector (con1, con2, con3, con4) para evitar conflictos de variables y asegurar la inicialización correcta.
|
||||
'
|
||||
' **En `RDCConnector.bas`:**
|
||||
'
|
||||
' * **Corrección de shadowing de `config`:** Se modificó `LoadConfigMap(DB)` para asignar directamente a la variable de clase `config` (eliminando `Dim` local), resolviendo el problema de la `jdbcUrl` truncada y asegurando que cada `RDCConnector` use su configuración específica de manera persistente.
|
||||
' * **Reordenamiento y robustecimiento de `Initialize`:**
|
||||
' * **Carga de `config`:** Se asegura que `config` se cargue completamente en la variable de clase antes de cualquier operación del pool.
|
||||
' * **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.
|
||||
' * **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
|
||||
' - Se agregó que se puedan configurar en el config.properties los siguientes parametros:
|
||||
' - - setInitialPoolSize = 3
|
||||
' - - setMinPoolSize = 2
|
||||
' - - setMaxPoolSize = 5
|
||||
' - Se agregaron en duro a RDConnector los siguientes parametros:
|
||||
' - - setMaxIdleTime <-- Tiempo máximo de inactividad de la conexión.
|
||||
' - - setMaxConnectionAge <-- Tiempo de vida máximo de una conexión.
|
||||
' - - setCheckoutTimeout <-- Tiempo máximo de espera por una conexión.
|
||||
' - Se agregó en el config.properties, al final del "JdbcUrl" este parametro, que le indica al servidor de Oracle
|
||||
' - el nombre del cliente que se está conectando "?v$session.program=jRDC_Multi"
|
||||
' - VERSION 5.09.08
|
||||
' - Se cambio el codigo para que en lugar de esperar un mapa con los parametros del query y nombres de los parametros (par1, par2, etc) para definir el ordenamiento, ahora se espera una lista [1,"2",3], y el orden de los parametros se toma directamente del orden en el que se mandan, de la misma forma que en B4A.
|
||||
' - VERSION 5.09.04
|
||||
' - Se cambio el nombre del handler de B4X a DBHandlerB4X.
|
||||
' - Se quitaron los handlers que ya no servian.
|
||||
' - VERSION 5.09.01
|
||||
' - Se corrigieron errores en "Manager".
|
||||
' - Se cambiaron nombres de handlers.
|
||||
' - Se corrigio un error en la ruta de "www/login.html".
|
||||
' - VERSION 5.08.31
|
||||
' - Se corrigio que no avisaba cuando el query no requeria parametros y si se enviaban (en el JSONHandler)
|
||||
' - VERSION 5.08.30
|
||||
' - Se cambiaron los 4 handlers de B4A a uno solo que toma el DB de la ruta automáticamente.
|
||||
' - Se agregaron validaciones del numero de parametros y si el query no los requiere o se dan de mas o de menos, manda un error especificando eso, ya no se reciben errores directos de la base de datos, esto fue tanto para B4A como para JSON.
|
||||
' - Se modificó el Readme.md para incluir todos estos cambios.
|
||||
' - VERSION 5.08.25
|
||||
' - Se modificaron los archivos de reinicio de los servicios (servidor y Bow) y se cambio el menu del "manager" para que a seccion de "reload" incluya la liga a reinciar Bow.
|
||||
' - VERSION 5.08.02
|
||||
' - Se hizo un cambio para tratar de que las conexiones se "identifiquen" con Oracle y Jorge pueda saber que conexiones/recursos estamos ocupando
|
||||
' - VERSION 4.11.14
|
||||
' - Se agregó el parametro "setMaxPoolSize=5" para que solo genere 5 conexiones a la base de datos, antes generaba 15.
|
||||
' - Se quitaron lineas previamente comentadas.
|
||||
' - VERSION 4.11.09
|
||||
' - Commit inicial on Nov 9, 2024
|
||||
|
||||
End Sub
|
||||
@@ -32,8 +32,7 @@ Public Sub Handle(req As ServletRequest, resp As ServletResponse)
|
||||
|
||||
Try
|
||||
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)
|
||||
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>")
|
||||
|
||||
164
DBHandlerB4X.bas
164
DBHandlerB4X.bas
@@ -17,7 +17,7 @@ Sub Class_Globals
|
||||
' La siguiente sección de constantes y utilidades se compila condicionalmente
|
||||
' 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.
|
||||
#if VERSION1
|
||||
' #if VERSION1
|
||||
' 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 _
|
||||
,T_DOUBLE = 6, T_BOOLEAN = 7, T_BLOB = 8 As Byte
|
||||
@@ -25,7 +25,7 @@ Sub Class_Globals
|
||||
Private bc As ByteConverter
|
||||
' Utilidad para comprimir/descomprimir streams de datos (usado en V1).
|
||||
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
|
||||
' 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.
|
||||
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}"$
|
||||
Log(ErrorMsg)
|
||||
SendPlainTextError(resp, 400, ErrorMsg) ' Envía una respuesta de error al cliente.
|
||||
' No se llama a CleanupAndLog aquí, ya que el contador de peticiones no se ha incrementado
|
||||
' y no se ha obtenido ninguna conexión del pool.
|
||||
Return ' Termina la ejecución del handler.
|
||||
Main.LogServerError("ERROR", "DBHandlerB4X.Handle", ErrorMsg, dbKey, Null, req.RemoteAddress) ' <-- Nuevo Log
|
||||
SendPlainTextError(resp, 400, ErrorMsg)
|
||||
Return
|
||||
End If
|
||||
' === 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)
|
||||
Return ' Salida temprana si hay un error.
|
||||
End If
|
||||
#if VERSION1
|
||||
' #if VERSION1
|
||||
' Estas ramas se compilan solo si #if VERSION1 está activo (para protocolo antiguo).
|
||||
Else if method = "query" Then
|
||||
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)
|
||||
Return
|
||||
End If
|
||||
#end if
|
||||
' #end if
|
||||
Else if method = "batch2" Then
|
||||
' Ejecuta un lote de comandos (INSERT, UPDATE, DELETE) utilizando el protocolo V2.
|
||||
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.
|
||||
End If
|
||||
Else
|
||||
' Si el método solicitado no es reconocido, se registra un error y se envía una respuesta adecuada.
|
||||
Log("Unknown method: " & method)
|
||||
Dim ErrorMsg As String = "Unknown method: " & method
|
||||
Log(ErrorMsg)
|
||||
Main.LogServerError("ERROR", "DBHandlerB4X.Handle", ErrorMsg, dbKey, method, req.RemoteAddress) ' <-- Nuevo Log
|
||||
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
|
||||
CleanupAndLog(dbKey, q, duration, req.RemoteAddress, requestsBeforeDecrement, poolBusyConnectionsForLog, con)
|
||||
Return ' Salida temprana.
|
||||
Return
|
||||
End If
|
||||
|
||||
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.
|
||||
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.
|
||||
q = "error_in_b4x_handler" ' Aseguramos un valor para 'q' en caso de excepción.
|
||||
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
|
||||
Dim errorMessage As String = $"El comando '${cmd.Name}' no fue encontrado en el config.properties de '${DB}'."$
|
||||
Log(errorMessage)
|
||||
Main.LogServerError("ERROR", "DBHandlerB4X.ExecuteQuery2", errorMessage, DB, cmd.Name, Null)
|
||||
' Envía un error 400 (Bad Request) al cliente informando del problema.
|
||||
SendPlainTextError(resp, 400, errorMessage)
|
||||
Return "error" ' Retorna un texto para el log.
|
||||
End If
|
||||
' <<< FIN NUEVA VALIDACIÓN >>>
|
||||
|
||||
' --- INICIO VALIDACIÓN DE PARÁMETROS ---
|
||||
' Comprueba si el SQL espera parámetros o si se recibieron parámetros.
|
||||
If sqlCommand.Contains("?") Or (cmd.Parameters <> Null And cmd.Parameters.Length > 0) Then
|
||||
' Cuenta cuántos '?' hay en la sentencia SQL para saber cuántos parámetros se esperan.
|
||||
Dim expectedParams As Int = sqlCommand.Length - sqlCommand.Replace("?", "").Length
|
||||
' Cuenta cuántos parámetros se recibieron.
|
||||
Dim receivedParams As Int
|
||||
If cmd.Parameters = Null Then receivedParams = 0 Else receivedParams = cmd.Parameters.Length
|
||||
|
||||
' 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
|
||||
' <<< INICIO VALIDACIÓN DE PARÁMETROS CENTRALIZADA >>>
|
||||
' Convertimos el array de Object() de cmd.Parameters a una List para la utilidad de validación.
|
||||
Dim paramsAsList As List
|
||||
paramsAsList.Initialize
|
||||
If cmd.Parameters <> Null Then
|
||||
For Each p As Object In cmd.Parameters
|
||||
paramsAsList.Add(p)
|
||||
Next
|
||||
End If
|
||||
' --- FIN VALIDACIÓN ---
|
||||
|
||||
' Ejecuta la consulta SQL con los parámetros proporcionados.
|
||||
Dim rs As ResultSet = con.ExecQuery2(sqlCommand, cmd.Parameters)
|
||||
Dim validationResult As ParameterValidationResult = ParameterValidationUtils.ValidateAndAdjustParameters(cmd.Name, DB, sqlCommand, paramsAsList, Connector.IsParameterToleranceEnabled)
|
||||
|
||||
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).
|
||||
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.
|
||||
Dim errorMessage As String = $"El comando '${cmd.Name}' no fue encontrado en el config.properties de '${DB}'."$
|
||||
Log(errorMessage)
|
||||
Main.LogServerError("ERROR", "DBHandlerB4X.ExecuteBatch2", errorMessage, DB, cmd.Name, Null)
|
||||
SendPlainTextError(resp, 400, errorMessage)
|
||||
Return "error"
|
||||
End If
|
||||
' <<< FIN NUEVA VALIDACIÓN >>>
|
||||
|
||||
' --- INICIO VALIDACIÓN DE PARÁMETROS DENTRO DEL BATCH ---
|
||||
If sqlCommand.Contains("?") Or (cmd.Parameters <> Null And cmd.Parameters.Length > 0) Then
|
||||
Dim expectedParams As Int = sqlCommand.Length - sqlCommand.Replace("?", "").Length
|
||||
Dim receivedParams As Int
|
||||
If cmd.Parameters = Null Then receivedParams = 0 Else receivedParams = cmd.Parameters.Length
|
||||
' Si el número de parámetros no coincide, deshace la transacción y envía error.
|
||||
If expectedParams <> receivedParams Then
|
||||
con.Rollback
|
||||
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
|
||||
' <<< INICIO VALIDACIÓN DE PARÁMETROS CENTRALIZADA DENTRO DEL BATCH >>>
|
||||
' Convertimos el array de Object() de cmd.Parameters a una List para la utilidad de validación.
|
||||
Dim paramsAsList As List
|
||||
paramsAsList.Initialize
|
||||
If cmd.Parameters <> Null Then
|
||||
For Each p As Object In cmd.Parameters
|
||||
paramsAsList.Add(p)
|
||||
Next
|
||||
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
|
||||
|
||||
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.
|
||||
con.Rollback ' Se deshacen todos los cambios hechos en la transacció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.
|
||||
End Try
|
||||
|
||||
@@ -410,7 +413,7 @@ End Sub
|
||||
|
||||
' --- 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.
|
||||
#if VERSION1
|
||||
'#if VERSION1
|
||||
|
||||
' 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
|
||||
@@ -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.
|
||||
Dim errorMessage As String = $"El comando '${queryName}' no fue encontrado en el config.properties de '${DB}'."$
|
||||
Log(errorMessage)
|
||||
Main.LogServerError("ERROR", "DBHandlerB4X.ExecuteBatch (V1)", errorMessage, DB, queryName, Null)
|
||||
SendPlainTextError(resp, 400, errorMessage)
|
||||
Return "error"
|
||||
End If
|
||||
' <<< FIN NUEVA VALIDACIÓN >>>
|
||||
' <<< FIN NUEVA VALIDACIÓN >>>
|
||||
|
||||
' --- INICIO VALIDACIÓN DE PARÁMETROS DENTRO DEL BATCH (V1) ---
|
||||
If sqlCommand.Contains("?") Or (params <> Null And params.Size > 0) Then
|
||||
Dim expectedParams As Int = sqlCommand.Length - sqlCommand.Replace("?", "").Length
|
||||
Dim receivedParams As Int
|
||||
If params = Null Then receivedParams = 0 Else receivedParams = params.Size
|
||||
' <<< INICIO VALIDACIÓN DE PARÁMETROS CENTRALIZADA DENTRO DEL BATCH (V1) >>>
|
||||
Dim validationResult As ParameterValidationResult = ParameterValidationUtils.ValidateAndAdjustParameters(queryName, DB, sqlCommand, params, Connector.IsParameterToleranceEnabled)
|
||||
|
||||
If expectedParams <> receivedParams Then
|
||||
con.Rollback
|
||||
Dim errorMessage As String = $"Número de parametros equivocado para "${queryName}". Se esperaban ${expectedParams} y se recibieron ${receivedParams}."$
|
||||
Log(errorMessage)
|
||||
SendPlainTextError(resp, 400, errorMessage)
|
||||
Return "error"
|
||||
End If
|
||||
End If
|
||||
' --- FIN VALIDACIÓN ---
|
||||
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, 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
|
||||
|
||||
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
|
||||
con.Rollback
|
||||
Log(LastException)
|
||||
Main.LogServerError("ERROR", "DBHandlerB4X.ExecuteBatch (V1)", LastException.Message, DB, "batch_execution_error_v1", Null)
|
||||
SendPlainTextError(resp, 500, LastException.Message)
|
||||
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
|
||||
Dim errorMessage As String = $"El comando '${queryName}' no fue encontrado en el config.properties de '${DB}'."$
|
||||
Log(errorMessage)
|
||||
Main.LogServerError("ERROR", "DBHandlerB4X.ExecuteQuery (V1)", errorMessage, DB, queryName, Null)
|
||||
SendPlainTextError(resp, 400, errorMessage)
|
||||
Return "error"
|
||||
End If
|
||||
' <<< FIN NUEVA VALIDACIÓN >>>
|
||||
' <<< FIN NUEVA VALIDACIÓN >>>
|
||||
|
||||
' --- INICIO VALIDACIÓN DE PARÁMETROS (V1) ---
|
||||
If theSql.Contains("?") Or (params <> Null And params.Size > 0) Then
|
||||
Dim expectedParams As Int = theSql.Length - theSql.Replace("?", "").Length
|
||||
Dim receivedParams As Int
|
||||
If params = Null Then receivedParams = 0 Else receivedParams = params.Size
|
||||
' <<< INICIO VALIDACIÓN DE PARÁMETROS CENTRALIZADA (V1) >>>
|
||||
Dim validationResult As ParameterValidationResult = ParameterValidationUtils.ValidateAndAdjustParameters(queryName, DB, theSql, params, Connector.IsParameterToleranceEnabled)
|
||||
|
||||
If expectedParams <> receivedParams Then
|
||||
Dim errorMessage As String = $"Número de parametros equivocado para "${queryName}". Se esperaban ${expectedParams} y se recibieron ${receivedParams}."$
|
||||
Log(errorMessage)
|
||||
SendPlainTextError(resp, 400, errorMessage)
|
||||
Return "error"
|
||||
End If
|
||||
End If
|
||||
' --- FIN VALIDACIÓN ---
|
||||
If validationResult.Success = False Then
|
||||
SendPlainTextError(resp, 400, validationResult.ErrorMessage)
|
||||
Return "error" ' Salida temprana si la validación falla.
|
||||
End If
|
||||
|
||||
' Ejecuta la consulta.
|
||||
Dim rs As ResultSet = con.ExecQuery2(theSql, params)
|
||||
' Ejecuta la consulta con la lista de parámetros validada.
|
||||
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
|
||||
|
||||
@@ -691,7 +685,7 @@ Private Sub ReadList(in As InputStream) As List
|
||||
Return l1
|
||||
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.
|
||||
' 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
|
||||
' 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.
|
||||
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 Sub
|
||||
@@ -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.
|
||||
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
|
||||
' 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)
|
||||
Return ' Salida temprana si no hay JSON válido.
|
||||
Return
|
||||
End If
|
||||
|
||||
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.
|
||||
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
|
||||
CleanupAndLog(finalDbKey, queryNameForLog, duration, req.RemoteAddress, requestsBeforeDecrement, poolBusyConnectionsForLog, con)
|
||||
Return ' Salida temprana si la DB no es válida.
|
||||
Return
|
||||
End If
|
||||
|
||||
con = Connector.GetConnection(finalDbKey) ' ¡La conexión a la BD se obtiene aquí del pool de conexiones!
|
||||
@@ -135,31 +138,30 @@ Sub Handle(req As ServletRequest, resp As ServletResponse)
|
||||
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}'."$
|
||||
Log(errorMessage)
|
||||
Main.LogServerError("ERROR", "DBHandlerJSON.Handle", errorMessage, finalDbKey, queryNameForLog, req.RemoteAddress) ' <-- Nuevo Log
|
||||
SendErrorResponse(resp, 400, errorMessage)
|
||||
duration = DateTime.Now - start
|
||||
CleanupAndLog(finalDbKey, queryNameForLog, duration, req.RemoteAddress, requestsBeforeDecrement, poolBusyConnectionsForLog, con)
|
||||
Return ' Salida temprana.
|
||||
Return
|
||||
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
|
||||
' Validación de parámetros para ExecuteQuery.
|
||||
If sqlCommand.Contains("?") Or paramsList.Size > 0 Then
|
||||
Dim expectedParams As Int = sqlCommand.Length - sqlCommand.Replace("?", "").Length
|
||||
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.
|
||||
' --- 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.
|
||||
End If
|
||||
|
||||
Dim rs As ResultSet
|
||||
' Ejecuta la consulta SQL con la lista de parámetros validada.
|
||||
rs = con.ExecQuery2(sqlCommand, validationResult.ParamsToExecute)
|
||||
' --- FIN VALIDACIÓN DE PARÁMETROS CENTRALIZADA ---
|
||||
|
||||
Dim ResultList As List
|
||||
ResultList.Initialize ' Lista para almacenar los resultados de la consulta.
|
||||
Dim jrs As JavaObject = rs ' Objeto Java subyacente del ResultSet para metadatos.
|
||||
@@ -180,29 +182,29 @@ Sub Handle(req As ServletRequest, resp As ServletResponse)
|
||||
SendSuccessResponse(resp, CreateMap("result": ResultList)) ' Envía la respuesta JSON de éxito.
|
||||
|
||||
Else If execType.ToLowerCase = "executecommand" Then
|
||||
' Validación de parámetros para ExecuteCommand.
|
||||
If sqlCommand.Contains("?") Then
|
||||
Dim expectedParams As Int = sqlCommand.Length - sqlCommand.Replace("?", "").Length
|
||||
Dim receivedParams As Int = paramsList.Size
|
||||
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
|
||||
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.
|
||||
' --- 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.
|
||||
End If
|
||||
|
||||
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
|
||||
' Si el tipo de ejecución no es reconocido.
|
||||
SendErrorResponse(resp, 400, "Parámetro 'exec' inválido. '" & execType & "' no es un valor permitido.")
|
||||
Dim ErrorMsg As String = "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.
|
||||
End If
|
||||
|
||||
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.
|
||||
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.
|
||||
queryNameForLog = "error_processing_json" ' Para registrar que hubo un error en el log.
|
||||
End Try ' --- FIN: Bloque Try principal ---
|
||||
|
||||
@@ -11,13 +11,19 @@ DriverClass=oracle.jdbc.driver.OracleDriver
|
||||
#GOHAN ---> server
|
||||
#JdbcUrl=jdbc:oracle:thin:@//10.0.0.205: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
|
||||
MinPoolSize=2
|
||||
MaxPoolSize=10
|
||||
AcquireIncrement=5
|
||||
MaxPoolSize=15
|
||||
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
|
||||
User=SALMA
|
||||
@@ -50,7 +56,7 @@ Debug=true
|
||||
sql.traeConexion=select 'DB2' as conexion from dual
|
||||
sql.select_soporte=select * from GUNA.soporte
|
||||
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_GV2=select cat_ve_version from GUNA.cat_version
|
||||
sql.selectAlmacen=select * from cat_almacen where cat_al_id = ?
|
||||
|
||||
@@ -11,20 +11,21 @@ DriverClass=oracle.jdbc.driver.OracleDriver
|
||||
#GOHAN ---> server
|
||||
#JdbcUrl=jdbc:oracle:thin:@//10.0.0.205: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
|
||||
MinPoolSize=2
|
||||
MaxPoolSize=10
|
||||
AcquireIncrement=5
|
||||
MaxPoolSize=15
|
||||
AcquireIncrement=2
|
||||
|
||||
# SVR-KEYMON-PRODUCCION--> Usuario
|
||||
#User=GUNA
|
||||
#Password=GUNAD2015M
|
||||
User=SALMA
|
||||
Password=SALMAD2016M
|
||||
|
||||
#User=TORRADOCONAUTO
|
||||
#Password=TORRADOCONAUTOD2016M
|
||||
|
||||
User=TORRADOCONAUTO
|
||||
Password=TORRADOCONAUTOD2016M
|
||||
|
||||
#--> Puertos
|
||||
#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_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_GUNA=select CAT_AG_ID, CAT_AG_NOMBRE from GUNA.cat_agencias order by CAT_AG_NOMBRE
|
||||
|
||||
@@ -11,13 +11,13 @@ DriverClass=oracle.jdbc.driver.OracleDriver
|
||||
#GOHAN ---> server
|
||||
#JdbcUrl=jdbc:oracle:thin:@//10.0.0.205: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
|
||||
MinPoolSize=2
|
||||
MaxPoolSize=10
|
||||
AcquireIncrement=5
|
||||
MaxPoolSize=15
|
||||
AcquireIncrement=2
|
||||
|
||||
# SVR-KEYMON-PRODUCCION--> Usuario
|
||||
User=SALMA
|
||||
@@ -31,7 +31,7 @@ Password=SALMAD2016M
|
||||
#SAC - DFR - MDA / GOHAN -->COBRANZA
|
||||
#ServerPort=1783
|
||||
#GUNA - SALMA - DURAKELO - DBC / SVR-KEYMON-PRODUCCION --> DISTRIBUIDORAS
|
||||
ServerPort=9000
|
||||
ServerPort=9010
|
||||
#CMG - TORRADO / TRUNKS -->COBRANZA/ GM
|
||||
#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_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_GUNA=select CAT_AG_ID, CAT_AG_NOMBRE from GUNA.cat_agencias order by CAT_AG_NOMBRE
|
||||
|
||||
@@ -13,11 +13,17 @@ DriverClass=oracle.jdbc.driver.OracleDriver
|
||||
#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
|
||||
|
||||
# Configuración del pool de conexiones para DB1
|
||||
# Configuración del pool de conexiones
|
||||
InitialPoolSize=3
|
||||
MinPoolSize=2
|
||||
MaxPoolSize=10
|
||||
AcquireIncrement=5
|
||||
MaxPoolSize=15
|
||||
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
|
||||
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.traeConexion2=select 'DB1' as conexion 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_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 = ?
|
||||
|
||||
113
Manager.bas
113
Manager.bas
@@ -154,14 +154,14 @@ Sub Handle(req As ServletRequest, resp As ServletResponse)
|
||||
|
||||
Dim newPoolStats As Map = newRDC.GetPoolStats
|
||||
sbTemp.Append($" -> ${dbKey}: Nuevo conector inicializado. Conexiones: ${newPoolStats.Get("TotalConnections")}"$).Append("<br>" & CRLF)
|
||||
|
||||
|
||||
Catch
|
||||
sbTemp.Append($" -> ERROR CRÍTICO al inicializar nuevo conector para ${dbKey}: ${LastException.Message}"$).Append("<br>" & CRLF)
|
||||
reloadSuccessful = False
|
||||
Exit ' Si uno falla, abortamos la recarga completa para evitar un estado inconsistente.
|
||||
End Try
|
||||
Next
|
||||
|
||||
|
||||
sb.Append(sbTemp.ToString) ' Añadimos los logs acumulados de la inicialización al StringBuilder principal.
|
||||
|
||||
If reloadSuccessful Then
|
||||
@@ -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))
|
||||
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
|
||||
|
||||
|
||||
69
ParameterValidationUtils.bas
Normal file
69
ParameterValidationUtils.bas
Normal 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
|
||||
@@ -31,6 +31,9 @@ Sub Class_Globals
|
||||
' Almacena la configuración completa (DriverClass, JdbcUrl, User, Password, InitialPoolSize, etc.)
|
||||
' cargada de su respectivo archivo .properties.
|
||||
Public config As Map
|
||||
|
||||
' Indica si la tolerancia a parámetros de más está activa.
|
||||
Public IsParameterToleranceEnabled As Boolean
|
||||
End Sub
|
||||
|
||||
' Subrutina de inicialización para el conector de una base de datos específica.
|
||||
@@ -44,6 +47,16 @@ Public Sub Initialize(DB As String)
|
||||
' Es CRUCIAL que se asigne a la variable de CLASE 'config' (sin 'Dim' local)
|
||||
' para que la configuración cargada del archivo sea persistente para esta instancia del conector.
|
||||
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.
|
||||
' Esto es esencial para capturar cualquier error crítico que impida la conexión inicial a la base de datos.
|
||||
@@ -78,7 +91,7 @@ Public Sub Initialize(DB As String)
|
||||
' 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.
|
||||
' 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!
|
||||
|
||||
' PASO 4: Forzar la creación de conexiones iniciales y verificar el estado.
|
||||
@@ -138,7 +151,9 @@ Public Sub Initialize(DB As String)
|
||||
Catch
|
||||
' 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.
|
||||
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
|
||||
|
||||
' 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.
|
||||
commands = Main.commandsMap.Get(DB).As(Map)
|
||||
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
|
||||
Return commands.Get("sql." & Key) ' Retorna la sentencia SQL.
|
||||
End Sub
|
||||
@@ -285,12 +302,16 @@ Public Sub GetPoolStats As Map
|
||||
|
||||
Catch
|
||||
' 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)
|
||||
End Try
|
||||
Else
|
||||
' 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.")
|
||||
End If
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ Sub Handle(req As ServletRequest, resp As ServletResponse)
|
||||
' Dim con As SQL = Main.rdcConnectorDB1.GetConnection("")
|
||||
Dim con As SQL = Main.Connectors.Get("DB1").As(RDCConnector).GetConnection("")
|
||||
resp.Write("Connection successful.</br></br>")
|
||||
Private estaDB As String = ""
|
||||
Dim estaDB As String = ""
|
||||
Log(Main.listaDeCP)
|
||||
For i = 0 To Main.listaDeCP.Size - 1
|
||||
If Main.listaDeCP.get(i) <> "" Then estaDB = "." & Main.listaDeCP.get(i)
|
||||
|
||||
136
jRDC_Multi.b4j
136
jRDC_Multi.b4j
@@ -32,9 +32,10 @@ Library8=jsql
|
||||
Library9=bcrypt
|
||||
Module1=Cambios
|
||||
Module10=Manager
|
||||
Module11=ping
|
||||
Module12=RDCConnector
|
||||
Module13=TestHandler
|
||||
Module11=ParameterValidationUtils
|
||||
Module12=ping
|
||||
Module13=RDCConnector
|
||||
Module14=TestHandler
|
||||
Module2=ChangePassHandler
|
||||
Module3=DBHandlerB4X
|
||||
Module4=DBHandlerJSON
|
||||
@@ -45,7 +46,7 @@ Module8=LoginHandler
|
||||
Module9=LogoutHandler
|
||||
NumberOfFiles=10
|
||||
NumberOfLibraries=9
|
||||
NumberOfModules=13
|
||||
NumberOfModules=14
|
||||
Version=10.3
|
||||
@EndOfDesignText@
|
||||
'Non-UI application (console / server application)
|
||||
@@ -53,7 +54,7 @@ Version=10.3
|
||||
#Region Project Attributes
|
||||
#CommandLineArgs:
|
||||
#MergeLibraries: True
|
||||
' VERSION 5.09.15
|
||||
' VERSION 5.09.16
|
||||
'###########################################################################################################
|
||||
'###################### 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).
|
||||
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.
|
||||
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
|
||||
|
||||
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) ===
|
||||
' DB1 siempre usa el archivo 'config.properties' por defecto.
|
||||
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".
|
||||
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.
|
||||
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}"$)
|
||||
Try
|
||||
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".
|
||||
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.
|
||||
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}"$)
|
||||
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) ===
|
||||
' 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
|
||||
' Procesa 'config.DB2.properties'
|
||||
If cpFiles.Get(i) = "config.DB2.properties" Then
|
||||
Dim con2 As RDCConnector ' Declara una variable específica y única para el conector de DB2.
|
||||
con2.Initialize("DB2") ' Inicializa la instancia del conector para "DB2".
|
||||
Connectors.Put("DB2", con2) ' Asocia "DB2" con su instancia de RDCConnector.
|
||||
listaDeCP.Add("DB2") ' Añade "DB2" a la lista de bases de datos.
|
||||
Log("Main.AppStart: Conector 'DB2' inicializado exitosamente.")
|
||||
Try
|
||||
Dim con2 As RDCConnector
|
||||
con2.Initialize("DB2")
|
||||
Connectors.Put("DB2", con2)
|
||||
listaDeCP.Add("DB2")
|
||||
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
|
||||
' Procesa 'config.DB3.properties'
|
||||
If cpFiles.Get(i) = "config.DB3.properties" Then
|
||||
Dim con3 As RDCConnector ' Declara una variable específica y única para el conector de DB3.
|
||||
con3.Initialize("DB3") ' Inicializa la instancia del conector para "DB3".
|
||||
Connectors.Put("DB3", con3) ' Asocia "DB3" con su instancia de RDCConnector.
|
||||
listaDeCP.Add("DB3") ' Añade "DB3" a la lista de bases de datos.
|
||||
Log("Main.AppStart: Conector 'DB3' inicializado exitosamente.")
|
||||
Try
|
||||
Dim con3 As RDCConnector
|
||||
con3.Initialize("DB3")
|
||||
Connectors.Put("DB3", con3)
|
||||
listaDeCP.Add("DB3")
|
||||
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
|
||||
' Procesa 'config.DB4.properties'
|
||||
If cpFiles.Get(i) = "config.DB4.properties" Then
|
||||
Dim con4 As RDCConnector ' Declara una variable específica y única para el conector de DB4.
|
||||
con4.Initialize("DB4") ' Inicializa la instancia del conector para "DB4".
|
||||
Connectors.Put("DB4", con4) ' Asocia "DB4" con su instancia de RDCConnector.
|
||||
listaDeCP.Add("DB4") ' Añade "DB4" a la lista de bases de datos.
|
||||
Log("Main.AppStart: Conector 'DB4' inicializado exitosamente.")
|
||||
Try
|
||||
Dim con4 As RDCConnector
|
||||
con4.Initialize("DB4")
|
||||
Connectors.Put("DB4", con4)
|
||||
listaDeCP.Add("DB4")
|
||||
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
|
||||
Next
|
||||
Else
|
||||
@@ -261,6 +295,15 @@ Sub InitializeSQLiteDatabase
|
||||
' Inserta el usuario por defecto en la tabla 'users'.
|
||||
SQL1.ExecNonQuery2("INSERT INTO users (username, password_hash) VALUES (?, ?)", Array As Object(defaultUser, hashedPass))
|
||||
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
|
||||
' Si el archivo de la base de datos ya existe, simplemente se abre.
|
||||
SQL1.InitializeSQLite(File.DirApp, dbFileName, True)
|
||||
@@ -311,6 +354,19 @@ Sub InitializeSQLiteDatabase
|
||||
Log("Añadiendo columna 'handler_active_requests' a query_logs.")
|
||||
SQL1.ExecNonQuery("ALTER TABLE query_logs ADD COLUMN handler_active_requests INTEGER DEFAULT 0")
|
||||
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
|
||||
' >>> FIN: Lógica de migración (ALTER TABLE) <<<
|
||||
End If
|
||||
@@ -330,13 +386,33 @@ Public Sub LogQueryPerformance(QueryName As String, DurationMs As Long, DbKey As
|
||||
End Try
|
||||
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'.
|
||||
' Se ejecuta periódicamente (cada 10 minutos) para limpiar la tabla de logs.
|
||||
Sub TimerLogs_Tick
|
||||
Try
|
||||
borraArribaDe15000Logs ' Llama a la función para limpiar los logs.
|
||||
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 Sub
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ ModuleBookmarks10=
|
||||
ModuleBookmarks11=
|
||||
ModuleBookmarks12=
|
||||
ModuleBookmarks13=
|
||||
ModuleBookmarks14=
|
||||
ModuleBookmarks2=
|
||||
ModuleBookmarks3=
|
||||
ModuleBookmarks4=
|
||||
@@ -18,6 +19,7 @@ ModuleBreakpoints10=
|
||||
ModuleBreakpoints11=
|
||||
ModuleBreakpoints12=
|
||||
ModuleBreakpoints13=
|
||||
ModuleBreakpoints14=
|
||||
ModuleBreakpoints2=
|
||||
ModuleBreakpoints3=
|
||||
ModuleBreakpoints4=
|
||||
@@ -32,14 +34,15 @@ ModuleClosedNodes10=
|
||||
ModuleClosedNodes11=
|
||||
ModuleClosedNodes12=
|
||||
ModuleClosedNodes13=
|
||||
ModuleClosedNodes14=
|
||||
ModuleClosedNodes2=
|
||||
ModuleClosedNodes3=
|
||||
ModuleClosedNodes3=9,10,11,12,13,14,15,16
|
||||
ModuleClosedNodes4=
|
||||
ModuleClosedNodes5=
|
||||
ModuleClosedNodes6=
|
||||
ModuleClosedNodes7=
|
||||
ModuleClosedNodes8=
|
||||
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
|
||||
VisibleModules=3,4,12,1,2,5,10,6
|
||||
VisibleModules=3,4,13,1,10,11,14,2
|
||||
|
||||
Reference in New Issue
Block a user