mirror of
https://github.com/KeymonSoft/jRDC-Multi.git
synced 2026-04-17 12:56:23 +00:00
- feat(sqlite): Implementa optimización de SQLite (WAL e Índices) - fix(manager): Extiende el comando 'test' para verificar todos los pools de conexión configurados. - Mejoras al subsistema de logs y diagnóstico del servidor jRDC2-Multi. - Cambios principales: 1. Optimización del Rendimiento de SQLite (users.db): * Habilitación de WAL: Se implementó PRAGMA journal_mode=WAL y PRAGMA synchronous=NORMAL en `InitializeSQLiteDatabase`. Esto reduce la contención de disco y mejora el rendimiento de I/O en las escrituras transaccionales de logs por lotes. * Índices de logs: Se agregaron índices a las columnas `timestamp` y `duration_ms` en `query_logs`, y a `timestamp` en `errores`. Esto acelera drásticamente las operaciones de limpieza periódica (`borraArribaDe15000Logs`) y la generación de reportes de consultas lentas (`slowqueries`). 2. Mejora del Comando de Diagnóstico 'test': * Se corrigió el comando `manager?command=test` para que no solo pruebe la conexión de `DB1`, sino que itere sobre `Main.listaDeCP` y fuerce la adquisición y liberación de una conexión (`GetConnection`) en *todos* los `RDCConnector` configurados (DB1, DB2, DB3, etc.). * La nueva lógica garantiza una prueba de vida rigurosa de cada pool C3P0, devolviendo un mensaje detallado del estado de conectividad y registrando un error crítico vía `LogServerError` si algún pool no responde.
475 lines
20 KiB
QBasic
475 lines
20 KiB
QBasic
B4J=true
|
|
Group=Default Group
|
|
ModulesStructureVersion=1
|
|
Type=Class
|
|
Version=10.3
|
|
@EndOfDesignText@
|
|
' Módulo de clase: Manager
|
|
' Este handler proporciona un panel de administración web para el servidor jRDC2-Multi.
|
|
' Permite monitorear el estado del servidor, recargar configuraciones de bases de datos,
|
|
' ver estadísticas de rendimiento, reiniciar servicios externos, y gestionar la autenticación de usuarios.
|
|
|
|
Sub Class_Globals
|
|
' Objeto para generar respuestas JSON. Se utiliza para mostrar mapas de datos de forma legible.
|
|
Dim j As JSONGenerator
|
|
' La clase BCrypt no se usa directamente en este módulo, pero se mantiene si hubiera planes futuros.
|
|
' Private bc As BCrypt
|
|
End Sub
|
|
|
|
' Subrutina de inicialización de la clase. Se llama cuando se crea un objeto de esta clase.
|
|
Public Sub Initialize
|
|
' No se requiere inicialización específica para esta clase en este momento.
|
|
End Sub
|
|
|
|
' Método principal que maneja las peticiones HTTP para el panel de administración.
|
|
' req: El objeto ServletRequest que contiene la información de la petición entrante.
|
|
' resp: El objeto ServletResponse para construir y enviar la respuesta al cliente.
|
|
' Módulo de clase: Manager
|
|
' ... (tu código de Class_Globals e Initialize se queda igual) ...
|
|
|
|
' Método principal que maneja las peticiones HTTP para el panel de administración.
|
|
' Refactorizado para funcionar como una API con un frontend estático.
|
|
Sub Handle(req As ServletRequest, resp As ServletResponse)
|
|
|
|
' --- 1. Bloque de Seguridad ---
|
|
If req.GetSession.GetAttribute2("user_is_authorized", False) = False Then
|
|
resp.SendRedirect("/login")
|
|
Return
|
|
End If
|
|
|
|
Dim Command As String = req.GetParameter("command")
|
|
|
|
' --- 2. Servidor de la Página Principal ---
|
|
If Command = "" Then
|
|
Try
|
|
resp.ContentType = "text/html; charset=utf-8"
|
|
resp.Write(File.ReadString(File.DirApp, "www/manager.html"))
|
|
Catch
|
|
resp.SendError(500, "Error: No se pudo encontrar el archivo principal del panel (www/manager.html). " & LastException.Message)
|
|
End Try
|
|
Return
|
|
End If
|
|
|
|
' --- 3. Manejo de Comandos como API ---
|
|
Select Command.ToLowerCase
|
|
|
|
' --- Comandos que devuelven JSON (Métricas del Pool) ---
|
|
Case "getstatsold"
|
|
resp.ContentType = "application/json; charset=utf-8"
|
|
Dim allPoolStats As Map
|
|
allPoolStats.Initialize
|
|
For Each dbKey As String In Main.listaDeCP
|
|
Dim connector As RDCConnector = Main.Connectors.Get(dbKey)
|
|
If connector.IsInitialized Then
|
|
allPoolStats.Put(dbKey, connector.GetPoolStats)
|
|
Else
|
|
allPoolStats.Put(dbKey, CreateMap("Error": "Conector no inicializado"))
|
|
End If
|
|
Next
|
|
j.Initialize(allPoolStats)
|
|
resp.Write(j.ToString)
|
|
Return
|
|
|
|
Case "getstats"
|
|
resp.ContentType = "application/json; charset=utf-8"
|
|
Dim allPoolStats As Map
|
|
|
|
' Leemos del caché global actualizado por el Timer SSE
|
|
allPoolStats = Main.LatestPoolStats
|
|
|
|
For Each dbKey As String In Main.listaDeCP
|
|
If allPoolStats.ContainsKey(dbKey) = False Then
|
|
allPoolStats.Put(dbKey, CreateMap("Error": "Métricas no disponibles/Pool no inicializado"))
|
|
End If
|
|
Next
|
|
|
|
j.Initialize(allPoolStats)
|
|
resp.Write(j.ToString)
|
|
Return
|
|
|
|
Case "slowqueries"
|
|
resp.ContentType = "application/json; charset=utf-8"
|
|
Dim results As List
|
|
results.Initialize
|
|
|
|
Try
|
|
' Verifica la existencia de la tabla de logs antes de consultar
|
|
Dim tableExists As Boolean = Main.SQL1.ExecQuerySingleResult($"SELECT name FROM sqlite_master WHERE type='table' AND name='query_logs';"$) <> Null
|
|
|
|
If tableExists = False Then
|
|
j.Initialize(CreateMap("message": "La tabla de logs ('query_logs') no existe. Habilita 'enableSQLiteLogs=1' en la configuración."))
|
|
resp.Write(j.ToString)
|
|
Return
|
|
End If
|
|
|
|
' Consulta las 20 queries más lentas de la última hora
|
|
Dim oneHourAgoMs As Long = DateTime.Now - 3600000
|
|
Dim rs As ResultSet = Main.SQL1.ExecQuery($"SELECT query_name, duration_ms, datetime(timestamp / 1000, 'unixepoch', 'localtime') as timestamp_local, db_key, client_ip, busy_connections, handler_active_requests FROM query_logs WHERE timestamp >= ${oneHourAgoMs} ORDER BY duration_ms DESC LIMIT 20"$)
|
|
|
|
Do While rs.NextRow
|
|
Dim row As Map
|
|
row.Initialize
|
|
row.Put("Query", rs.GetString("query_name"))
|
|
row.Put("Duracion_ms", rs.GetLong("duration_ms"))
|
|
row.Put("Fecha_Hora", rs.GetString("timestamp_local"))
|
|
row.Put("DB_Key", rs.GetString("db_key"))
|
|
row.Put("Cliente_IP", rs.GetString("client_ip"))
|
|
row.Put("Conexiones_Ocupadas", rs.GetInt("busy_connections"))
|
|
row.Put("Peticiones_Activas", rs.GetInt("handler_active_requests"))
|
|
results.Add(row)
|
|
Loop
|
|
rs.Close
|
|
|
|
Dim root As Map
|
|
root.Initialize
|
|
root.Put("data", results)
|
|
j.Initialize(root)
|
|
resp.Write(j.ToString)
|
|
|
|
Catch
|
|
Log("Error CRÍTICO al obtener queries lentas en Manager API: " & LastException.Message)
|
|
resp.Status = 500
|
|
|
|
Dim root As Map
|
|
root.Initialize
|
|
root.Put("data", results)
|
|
j.Initialize(root)
|
|
resp.Write(j.ToString)
|
|
End Try
|
|
Return
|
|
|
|
Case "logs", "totalrequests", "totalblocked"
|
|
resp.ContentType = "application/json; charset=utf-8"
|
|
Dim mp As Map
|
|
If Command = "logs" And GlobalParameters.mpLogs.IsInitialized Then mp = GlobalParameters.mpLogs
|
|
If Command = "totalrequests" And GlobalParameters.mpTotalRequests.IsInitialized Then mp = GlobalParameters.mpTotalRequests
|
|
If Command = "totalblocked" And GlobalParameters.mpBlockConnection.IsInitialized Then mp = GlobalParameters.mpBlockConnection
|
|
|
|
If mp.IsInitialized Then
|
|
j.Initialize(mp)
|
|
resp.Write(j.ToString)
|
|
Else
|
|
resp.Write("{}")
|
|
End If
|
|
Return
|
|
|
|
' --- Comandos que devuelven TEXTO PLANO ---
|
|
Case "ping"
|
|
resp.ContentType = "text/plain"
|
|
resp.Write($"Pong ($DateTime{DateTime.Now})"$)
|
|
Return
|
|
|
|
Case "reload"
|
|
resp.ContentType = "text/plain; charset=utf-8"
|
|
Dim sbTemp As StringBuilder
|
|
sbTemp.Initialize
|
|
|
|
' ***** LÓGICA DE RECARGA GRANULAR/SELECTIVA *****
|
|
Dim dbKeyToReload As String = req.GetParameter("db").ToUpperCase ' Leer parámetro 'db' opcional (ej: /manager?command=reload&db=DB3)
|
|
Dim targets As List ' Lista de DBKeys a recargar.
|
|
targets.Initialize
|
|
|
|
' 1. Determinar el alcance de la recarga (selectiva o total)
|
|
If dbKeyToReload.Length > 0 Then
|
|
' Recarga selectiva
|
|
If Main.listaDeCP.IndexOf(dbKeyToReload) = -1 Then
|
|
resp.Write($"ERROR: DBKey '${dbKeyToReload}' no es válida o no está configurada."$)
|
|
Return
|
|
End If
|
|
targets.Add(dbKeyToReload)
|
|
sbTemp.Append($"Iniciando recarga selectiva de ${dbKeyToReload} (Hot-Swap)..."$).Append(" " & CRLF)
|
|
Else
|
|
' Recarga completa (comportamiento por defecto)
|
|
targets.AddAll(Main.listaDeCP)
|
|
sbTemp.Append($"Iniciando recarga COMPLETA de configuración (Hot-Swap) ($DateTime{DateTime.Now})"$).Append(" " & CRLF)
|
|
End If
|
|
|
|
' 2. Deshabilitar el Timer de logs (si es necesario)
|
|
Dim oldTimerState As Boolean = Main.timerLogs.Enabled
|
|
If oldTimerState Then
|
|
Main.timerLogs.Enabled = False
|
|
sbTemp.Append(" -> Timer de limpieza de logs (SQLite) detenido temporalmente.").Append(" " & CRLF)
|
|
End If
|
|
|
|
Dim reloadSuccessful As Boolean = True
|
|
Dim oldConnectorsToClose As Map ' Guardaremos los conectores antiguos aquí.
|
|
oldConnectorsToClose.Initialize
|
|
|
|
' 3. Procesar solo los conectores objetivos
|
|
For Each dbKey As String In targets
|
|
sbTemp.Append($" -> Procesando recarga de ${dbKey}..."$).Append(CRLF)
|
|
Dim newRDC As RDCConnector
|
|
|
|
Try
|
|
' Crear el nuevo conector con la configuración fresca
|
|
newRDC.Initialize(dbKey)
|
|
|
|
' Adquirimos el lock para el reemplazo atómico
|
|
Main.MainConnectorsLock.RunMethod("lock", Null)
|
|
|
|
' Guardamos el conector antiguo (si existe)
|
|
Dim oldRDC As RDCConnector = Main.Connectors.Get(dbKey)
|
|
|
|
' Reemplazo atómico en el mapa global compartido
|
|
Main.Connectors.Put(dbKey, newRDC)
|
|
|
|
' Liberamos el bloqueo inmediatamente
|
|
Main.MainConnectorsLock.RunMethod("unlock", Null)
|
|
|
|
' Si había un conector antiguo, lo guardamos para cerrarlo después
|
|
If oldRDC.IsInitialized Then
|
|
oldConnectorsToClose.Put(dbKey, oldRDC)
|
|
End If
|
|
|
|
' 4. Actualizar el estado de logs (Granular)
|
|
Dim enableLogsSetting As Int = newRDC.config.GetDefault("enableSQLiteLogs", 0)
|
|
Dim isEnabled As Boolean = (enableLogsSetting = 1)
|
|
Main.SQLiteLoggingStatusByDB.Put(dbKey, isEnabled)
|
|
sbTemp.Append($" -> ${dbKey} recargado. Logs (config): ${isEnabled}"$).Append(CRLF)
|
|
|
|
Catch
|
|
' Si falla la inicialización del pool, no actualizamos Main.Connectors
|
|
|
|
' ¡CRÍTICO! Aseguramos que el lock se libere si hubo excepción antes de liberar.
|
|
If Main.MainConnectorsLock.RunMethod("isHeldByCurrentThread", Null).As(Boolean) Then
|
|
Main.MainConnectorsLock.RunMethod("unlock", Null)
|
|
End If
|
|
|
|
sbTemp.Append($" -> ERROR CRÍTICO al inicializar conector para ${dbKey}: ${LastException.Message}"$).Append(" " & CRLF)
|
|
reloadSuccessful = False
|
|
Exit
|
|
End Try
|
|
Next
|
|
|
|
' 5. Cerrar los pools antiguos liberados (FUERA del Lock)
|
|
If reloadSuccessful Then
|
|
For Each dbKey As String In oldConnectorsToClose.Keys
|
|
Dim oldRDC As RDCConnector = oldConnectorsToClose.Get(dbKey)
|
|
oldRDC.Close ' Cierre limpio del pool C3P0
|
|
sbTemp.Append($" -> Pool antiguo de ${dbKey} cerrado limpiamente."$).Append(" " & CRLF)
|
|
Next
|
|
|
|
' 6. Re-evaluar el estado global de Logs (CRÍTICO: debe revisar TODAS las DBs)
|
|
Main.IsAnySQLiteLoggingEnabled = False
|
|
|
|
For Each dbKey As String In Main.listaDeCP
|
|
' Revisamos el estado de log de CADA conector activo
|
|
If Main.SQLiteLoggingStatusByDB.GetDefault(dbKey, False) Then
|
|
Main.IsAnySQLiteLoggingEnabled = True
|
|
Exit
|
|
End If
|
|
Next
|
|
If Main.IsAnySQLiteLoggingEnabled Then
|
|
Main.timerLogs.Enabled = True
|
|
sbTemp.Append($" -> Timer de limpieza de logs ACTIVADO (estado global: HABILITADO)."$).Append(" " & CRLF)
|
|
Else
|
|
Main.timerLogs.Enabled = False
|
|
sbTemp.Append($" -> Timer de limpieza de logs DESHABILITADO (estado global: DESHABILITADO)."$).Append(" " & CRLF)
|
|
End If
|
|
sbTemp.Append($"¡Recarga de configuración completada con éxito!"$).Append(" " & CRLF)
|
|
Else
|
|
' Si falló, restauramos el estado del timer anterior.
|
|
If oldTimerState Then
|
|
Main.timerLogs.Enabled = True
|
|
sbTemp.Append(" -> Restaurando Timer de limpieza de logs al estado ACTIVO debido a fallo en recarga.").Append(" " & CRLF)
|
|
End If
|
|
sbTemp.Append($"¡ERROR: La recarga de configuración falló! Los conectores antiguos siguen activos."$).Append(" " & CRLF)
|
|
End If
|
|
resp.Write(sbTemp.ToString)
|
|
Return
|
|
Case "test"
|
|
resp.ContentType = "text/plain; charset=utf-8"
|
|
Dim sb As StringBuilder
|
|
sb.Initialize
|
|
sb.Append("--- INICIANDO PRUEBA DE CONECTIVIDAD A TODOS LOS POOLS CONFIGURADOS ---").Append(CRLF).Append(CRLF)
|
|
|
|
' Iteramos sobre la lista de DB Keys cargadas al inicio (DB1, DB2, etc.)
|
|
For Each dbKey As String In Main.listaDeCP
|
|
Dim success As Boolean = False
|
|
Dim errorMsg As String = ""
|
|
Dim con As SQL ' Conexión para la prueba
|
|
|
|
Try
|
|
' 1. Obtener el RDCConnector para esta DBKey
|
|
Dim connector As RDCConnector = Main.Connectors.Get(dbKey)
|
|
|
|
If connector.IsInitialized = False Then
|
|
errorMsg = "Conector no inicializado (revisa logs de AppStart)"
|
|
Else
|
|
' 2. Forzar la adquisición de una conexión del pool C3P0
|
|
con = connector.GetConnection(dbKey)
|
|
|
|
If con.IsInitialized Then
|
|
' 3. Si la conexión es válida, la cerramos inmediatamente para devolverla al pool
|
|
con.Close
|
|
success = True
|
|
Else
|
|
errorMsg = "La conexión devuelta no es válida (SQL.IsInitialized = False)"
|
|
End If
|
|
End If
|
|
|
|
Catch
|
|
' Capturamos cualquier excepción (ej. fallo de JDBC, timeout de C3P0)
|
|
errorMsg = LastException.Message
|
|
End Try
|
|
|
|
If success Then
|
|
sb.Append($"* ${dbKey}: Conexión adquirida y liberada correctamente."$).Append(CRLF)
|
|
Else
|
|
' Si falla, registramos el error para el administrador.
|
|
Main.LogServerError("ERROR", "Manager.TestCommand", $"Falló la prueba de conectividad para ${dbKey}: ${errorMsg}"$, dbKey, "test_command", req.RemoteAddress)
|
|
sb.Append($"[FALLO] ${dbKey}: ERROR CRÍTICO al obtener conexión. Mensaje: ${errorMsg}"$).Append(CRLF)
|
|
End If
|
|
Next
|
|
|
|
sb.Append(CRLF).Append("--- FIN DE PRUEBA DE CONEXIONES ---").Append(CRLF)
|
|
|
|
' Mantenemos la lista original de archivos de configuración cargados (esto es informativo)
|
|
sb.Append(CRLF).Append("Archivos de configuración cargados:").Append(CRLF)
|
|
For Each item As String In Main.listaDeCP
|
|
Dim configName As String = "config"
|
|
If item <> "DB1" Then configName = configName & "." & item
|
|
sb.Append($" -> Usando ${configName}.properties"$).Append(CRLF)
|
|
Next
|
|
|
|
resp.Write(sb.ToString)
|
|
Return
|
|
Case "rsx", "rpm2", "revivebow", "restartserver"
|
|
resp.ContentType = "text/plain; charset=utf-8"
|
|
Dim batFile As String
|
|
Select Command
|
|
Case "rsx": batFile = "start.bat"
|
|
Case "rpm2": batFile = "reiniciaProcesoPM2.bat"
|
|
Case "reviveBow": batFile = "reiniciaProcesoBow.bat"
|
|
Case "restartserver": batFile = "restarServer.bat" ' Nota: este bat no estaba definido, se usó el nombre del comando
|
|
End Select
|
|
|
|
Log($"Ejecutando ${File.DirApp}\${batFile}"$)
|
|
Try
|
|
Dim shl As Shell
|
|
shl.Initialize("shl","cmd", Array("/c", File.DirApp & "\" & batFile & " " & Main.srvr.Port))
|
|
shl.WorkingDirectory = File.DirApp
|
|
shl.Run(-1)
|
|
resp.Write($"Comando '${Command}' ejecutado. Script invocado: ${batFile}"$)
|
|
Catch
|
|
resp.Write($"Error al ejecutar el script para '${Command}': ${LastException.Message}"$)
|
|
End Try
|
|
Return
|
|
|
|
Case "paused", "continue"
|
|
resp.ContentType = "text/plain; charset=utf-8"
|
|
If Command = "paused" Then
|
|
GlobalParameters.IsPaused = 1
|
|
resp.Write("Servidor pausado.")
|
|
Else
|
|
GlobalParameters.IsPaused = 0
|
|
resp.Write("Servidor reanudado.")
|
|
End If
|
|
Return
|
|
|
|
Case "block", "unblock"
|
|
resp.ContentType = "text/plain; charset=utf-8"
|
|
Dim ip As String = req.GetParameter("IP")
|
|
|
|
If ip = "" Then
|
|
resp.Write("Error: El parámetro IP es requerido.")
|
|
Return
|
|
End If
|
|
|
|
If GlobalParameters.mpBlockConnection.IsInitialized Then
|
|
If Command = "block" Then
|
|
GlobalParameters.mpBlockConnection.Put(ip, ip)
|
|
resp.Write($"IP bloqueada: ${ip}"$)
|
|
Else
|
|
GlobalParameters.mpBlockConnection.Remove(ip)
|
|
resp.Write($"IP desbloqueada: ${ip}"$)
|
|
End If
|
|
Else
|
|
resp.Write("Error: El mapa de bloqueo no está inicializado.")
|
|
End If
|
|
Return
|
|
Case "getconfiginfo"
|
|
resp.ContentType = "text/plain; charset=utf-8"
|
|
Dim sbInfo As StringBuilder
|
|
sbInfo.Initialize
|
|
|
|
' sbInfo.Append($"--- CONFIGURACIÓN ACTUAL DEL SERVIDOR jRDC2-Multi ($DateTime{DateTime.Now}) ---"$).Append(CRLF).Append(CRLF)
|
|
|
|
Dim allKeys As List
|
|
allKeys.Initialize
|
|
allKeys.AddAll(Main.listaDeCP) ' DB1, DB2, ...
|
|
sbInfo.Append("======================================================================").Append(CRLF)
|
|
sbInfo.Append($"=== CONFIGURACIÓN jRDC2-Multi V$1.2{Main.VERSION} (ACTIVA) ($DateTime{DateTime.Now}) ==="$).Append(CRLF)
|
|
sbInfo.Append("======================================================================").Append(CRLF).Append(CRLF)
|
|
|
|
' ***** GLOSARIO DE PARÁMETROS CONFIGURABLES *****
|
|
sbInfo.Append("### GLOSARIO DE PARÁMETROS PERMITIDOS EN CONFIG.PROPERTIES ###").Append(CRLF)
|
|
sbInfo.Append("--------------------------------------------------").Append(CRLF)
|
|
|
|
sbInfo.Append("DriverClass: Clase del driver JDBC (ej: oracle.jdbc.driver.OracleDriver).").Append(CRLF)
|
|
sbInfo.Append("JdbcUrl: URL de conexión a la base de datos (IP, puerto, servicio).").Append(CRLF)
|
|
sbInfo.Append("User/Password: Credenciales de acceso a la BD.").Append(CRLF)
|
|
sbInfo.Append("ServerPort: Puerto de escucha del servidor B4J (solo lo toma de config.properties).").Append(CRLF)
|
|
sbInfo.Append("Debug: Si es 'true', los comandos SQL se recargan en cada petición (DESHABILITADO, USAR COMANDO RELOAD).").Append(CRLF)
|
|
sbInfo.Append("parameterTolerance: Define si se recortan (1) o se rechazan (0) los parámetros SQL sobrantes a los requeridos por el query.").Append(CRLF)
|
|
sbInfo.Append("enableSQLiteLogs: Control granular. Habilita (1) o deshabilita (0) la escritura de logs en users.db para esta DB.").Append(CRLF)
|
|
sbInfo.Append("InitialPoolSize: Conexiones que el pool establece al iniciar (c3p0).").Append(CRLF)
|
|
sbInfo.Append("MinPoolSize: Mínimo de conexiones inactivas que se mantendrán.").Append(CRLF)
|
|
sbInfo.Append("MaxPoolSize: Máximo de conexiones simultáneas permitido.").Append(CRLF)
|
|
sbInfo.Append("AcquireIncrement: Número de conexiones nuevas que se adquieren en lote al necesitar más.").Append(CRLF)
|
|
sbInfo.Append("MaxIdleTime: Tiempo máximo (segundos) de inactividad antes de cerrar una conexión.").Append(CRLF)
|
|
sbInfo.Append("MaxConnectionAge: Tiempo máximo de vida (segundos) de una conexión.").Append(CRLF)
|
|
sbInfo.Append("CheckoutTimeout: Tiempo máximo de espera (milisegundos) por una conexión disponible.").Append(CRLF)
|
|
sbInfo.Append(CRLF)
|
|
|
|
For Each dbKey As String In allKeys
|
|
|
|
' --- COMIENZA EL DETALLE POR CONECTOR ---
|
|
|
|
Dim connector As RDCConnector = Main.Connectors.Get(dbKey)
|
|
|
|
sbInfo.Append("--------------------------------------------------").Append(CRLF).Append(CRLF)
|
|
sbInfo.Append($"---------------- ${dbKey} ------------------"$).Append(CRLF).Append(CRLF)
|
|
sbInfo.Append("--------------------------------------------------").Append(CRLF).Append(CRLF)
|
|
If connector.IsInitialized Then
|
|
Dim configMap As Map = connector.config
|
|
|
|
sbInfo.Append($"DriverClass: ${configMap.GetDefault("DriverClass", "N/A")}"$).Append(CRLF)
|
|
sbInfo.Append($"JdbcUrl: ${configMap.GetDefault("JdbcUrl", "N/A")}"$).Append(CRLF)
|
|
sbInfo.Append($"User: ${configMap.GetDefault("User", "N/A")}"$).Append(CRLF)
|
|
sbInfo.Append($"ServerPort: ${configMap.GetDefault("ServerPort", "N/A")}"$).Append(CRLF).Append(CRLF)
|
|
|
|
sbInfo.Append("--- CONFIGURACIÓN DEL POOL (C3P0) ---").Append(CRLF)
|
|
sbInfo.Append($"InitialPoolSize: ${configMap.GetDefault("InitialPoolSize", 3)}"$).Append(CRLF)
|
|
sbInfo.Append($"MinPoolSize: ${configMap.GetDefault("MinPoolSize", 2)}"$).Append(CRLF)
|
|
sbInfo.Append($"MaxPoolSize: ${configMap.GetDefault("MaxPoolSize", 5)}"$).Append(CRLF)
|
|
sbInfo.Append($"AcquireIncrement: ${configMap.GetDefault("AcquireIncrement", 5)}"$).Append(CRLF)
|
|
sbInfo.Append($"MaxIdleTime (s): ${configMap.GetDefault("MaxIdleTime", 300)}"$).Append(CRLF)
|
|
sbInfo.Append($"MaxConnectionAge (s): ${configMap.GetDefault("MaxConnectionAge", 900)}"$).Append(CRLF)
|
|
sbInfo.Append($"CheckoutTimeout (ms): ${configMap.GetDefault("CheckoutTimeout", 60000)}"$).Append(CRLF).Append(CRLF)
|
|
|
|
sbInfo.Append("--- COMPORTAMIENTO ---").Append(CRLF)
|
|
sbInfo.Append($"Debug (Recarga Queries - DESHABILITADO): ${configMap.GetDefault("Debug", "false")}"$).Append(CRLF)
|
|
|
|
' Lectura explícita de las nuevas propiedades, asegurando un Int.
|
|
Dim tolerance As Int = configMap.GetDefault("parameterTolerance", 0).As(Int)
|
|
sbInfo.Append($"ParameterTolerance: ${tolerance} (0=Estricto, 1=Habilitado)"$).Append(CRLF)
|
|
|
|
Dim logsEnabled As Int = configMap.GetDefault("enableSQLiteLogs", 1).As(Int)
|
|
sbInfo.Append($"EnableSQLiteLogs: ${logsEnabled} (0=Deshabilitado, 1=Habilitado)"$).Append(CRLF)
|
|
|
|
sbInfo.Append(CRLF)
|
|
|
|
Else
|
|
sbInfo.Append($"ERROR: Conector ${dbKey} no inicializado o falló al inicio."$).Append(CRLF).Append(CRLF)
|
|
End If
|
|
Next
|
|
|
|
resp.Write(sbInfo.ToString)
|
|
Return
|
|
Case Else
|
|
resp.ContentType = "text/plain; charset=utf-8"
|
|
resp.SendError(404, $"Comando desconocido: '{Command}'"$)
|
|
Return
|
|
End Select
|
|
End Sub |