B4J=true Group=Default Group ModulesStructureVersion=1 Type=Class Version=4.19 @EndOfDesignText@ ' Módulo de clase: RDCConnector ' Esta clase gestiona el pool de conexiones a una base de datos específica. ' REFRACTORIZADO: Usa ConnectionPoolManager y delega a HikariCP. Sub Class_Globals ' --- Variables globales de la clase --- Private pool As ConnectionPoolManager ' Usa el Manager para la modularidad Private MyHikariPool As HikariConnectionPool ' <-- NUEVO: Pool dedicado a esta DBKey. Private DebugQueries As Boolean = False ' Bandera para activar/desactivar el modo de depuración Public commands As Map ' Comandos SQL cargados Public serverPort As Int Public usePool As Boolean = True Public config As Map ' Configuración completa (JdbcUrl, User, Password, etc.) Public IsParameterToleranceEnabled As Boolean ' Tolerancia a parámetros de más Dim driverProperties As Map ' CRÍTICO: Propiedades específicas del driver (MySQL statement caching) Private configLocation As String ' Ubicación del archivo de configuración Private poolProperties As Map End Sub ' Subrutina de inicialización para el conector de una base de datos específica. Public Sub Initialize(DB As String) ' Nota: Este código asume que MyHikariPool ya está declarado en Class_Globals Dim dbKey As String = DB ' Usaremos DB como la llave If DB.EqualsIgnoreCase("DB1") Then dbKey = "" poolProperties.Initialize driverProperties.Initialize ' PASO 1: Cargar la configuración desde el archivo .properties correspondiente. config = LoadConfigMap(dbKey) ' Aseguramos la carga en la variable de clase [1] ' Lectura de la configuración de tolerancia de parámetros Dim toleranceSetting As Int = config.GetDefault("parameterTolerance", 0).As(Int) IsParameterToleranceEnabled = (toleranceSetting = 1) If IsParameterToleranceEnabled Then Log($"RDCConnector.Initialize para ${dbKey}: Tolerancia a parámetros extras, HABILITADA."$) Else Log($"RDCConnector.Initialize para ${dbKey}: Tolerancia a parámetros extras, DESHABILITADA (modo estricto)."$) End If ' Bloque Try-Catch para la inicialización y configuración del pool. Try Dim driverClass As String = config.Get("DriverClass") Dim jdbcUrl As String = config.Get("JdbcUrl") Dim aUser As String = config.Get("User") Dim aPassword As String = config.Get("Password") Dim poolType As String = "Hikari" ' Forzamos Hikari ' *** INICIO DE LA LÓGICA DE PRECEDENCIA DE TAMAÑO (HIKARI) *** Dim maxSizeKey As String = $"pool.${poolType.ToLowerCase}.maximumpoolsize"$ Dim poolSizeString As String Dim poolSize As Int ' Intentamos leer el valor específico (pool.hikari.maximumpoolsize). If config.ContainsKey(maxSizeKey) Then poolSizeString = config.Get(maxSizeKey) poolSize = poolSizeString.As(Int) Else ' Si no está definido, usamos el default recomendado por Hikari (10). [2] poolSize = 10 End If If poolSize < 1 Then poolSize = 10 ' Mantenemos la sensatez Log($"RDCConnector: Usando MaximumPoolSize para ${poolType} calculado: ${poolSize}"$) ' *** PASO 2: INICIALIZA/CREA EL POOL LOCALMENTE (Decoupling CRÍTICO) *** If MyHikariPool.IsInitialized = False Then MyHikariPool.Initialize ' Inicializa el wrapper local ' Crea el pool subyacente (DataSource) en esta instancia dedicada. [3] MyHikariPool.CreatePool2(driverClass, jdbcUrl, aUser, aPassword, poolSize) ' PASO 3a: Cargar y filtrar SOLO las propiedades del Pool (ej. las que comienzan con 'pool.hikari.') LoadPoolProperties(poolType, config) ' PASO 3b: Aplicar propiedades de ESTABILIDAD (Pool Properties) If poolProperties.Size > 0 Then ' Aplicación directa al pool local. [4] CallSub2(MyHikariPool, "SetProperties", poolProperties) End If ' PASO 4: Cargar propiedades específicas del Driver (ej. Statement Caching) If config.ContainsKey("DriverShortName") Then LoadDriverProperties(config.Get("DriverShortName"), config) End If ' PASO 5: Aplicar propiedades de RENDIMIENTO (Driver Properties) If driverProperties.Size > 0 Then ' Aplicación directa al pool local. [5] CallSub2(MyHikariPool, "SetDriverProperties", driverProperties) Log($"RDCConnector.Initialize para ${DB}: {driverProperties.Size} propiedades del Driver aplicadas a HikariCP."$) End If ' PASO 6 (Prueba de vida): Forzar la creación de conexiones iniciales y verificar el estado. ' Esto garantiza el fail-fast. [6] Dim tempCon As SQL = MyHikariPool.GetConnection If tempCon.IsInitialized Then tempCon.Close End If ' Cargar configuración estática en el cache global Dim dbKeyToStore As String = DB If dbKeyToStore = "" Then dbKeyToStore = "DB1" ' Almacenamos el mapa completo (configuración estática + métricas dinámicas iniciales) en el cache global. ' GetPoolStats ahora usa MyHikariPool. Dim initialPoolStats As Map = GetPoolStats Main.LatestPoolStats.Put(dbKeyToStore, initialPoolStats) Catch ' Si ocurre un error durante la inicialización del pool o al forzar la conexión. 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) ' Si falla la inicialización, la instancia local MyHikariPool se queda inutilizada. ' Aquí podríamos considerar la opción de llamar a MyHikariPool.ClosePool para asegurar ' que no queden recursos parciales, aunque HikariCP debería manejarse con fail-fast. End Try ' Carga los comandos SQL predefinidos de esta base de datos en el mapa global 'commandsMap' de Main. If dbKey = "" Then dbKey = "DB1" LoadSQLCommands(config, dbKey) serverPort = config.Get("ServerPort") End Sub ' Carga el mapa de configuración Private Sub LoadConfigMap(DB As String) As Map Private DBX As String = "" If DB <> "" Then DBX = "." & DB Log($"RDCConnector.LoadConfigMap: Leemos el config${DBX}.properties"$) Return File.ReadMap("./", "config" & DBX & ".properties") End Sub ' Obtiene la sentencia SQL completa para un comando dado. Public Sub GetCommand(DB As String, Key As String) As String commands = Main.commandsMap.Get(DB).As(Map) If commands.ContainsKey("sql." & Key) = False Then Dim ErrorMsg As String = $"RDCConnector.GetCommand: *** Comando no encontrado: '${Key}' para DB: '${DB}' ***"$ Log(ErrorMsg) Main.LogServerError("ERROR", "RDCConnector.GetCommand", ErrorMsg, DB, Key, Null) End If Return commands.Get("sql." & Key) End Sub ' Obtiene una conexión SQL funcional del pool de conexiones. Public Sub GetConnection(DB As String) As SQL ' If DB.EqualsIgnoreCase("DB1") Then DB = "" ' If DebugQueries Then LoadSQLCommands(LoadConfigMap(DB), DB) ' Deshabilitado por defecto. [13] ' Devolvemos la conexión del pool local, si está inicializado. If MyHikariPool.IsInitialized Then Return MyHikariPool.GetConnection Else Log($"ERROR: Intento de obtener conexión de DBKey ${DB}, pero MyHikariPool no está inicializado."$) ' Devolver Null o lanzar excepción, dependiendo del manejo de errores deseado. Return Null End If ' ANTES: Return Main.ConnectionPoolManager1.GetConnection End Sub ' Carga todos los comandos SQL del mapa de configuración en el mapa global 'commandsMap' de Main. Private Sub LoadSQLCommands(config2 As Map, DB As String) Dim newCommands As Map newCommands.Initialize For Each k As String In config2.Keys If k.StartsWith("sql.") Then newCommands.Put(k, config2.Get(k)) End If Next commands = newCommands Main.commandsMap.Put(DB, commands) End Sub ' ** Delegación de estadísticas de C3P0 a HikariCP ** Public Sub GetPoolStats As Map Dim stats As Map stats.Initialize If MyHikariPool.IsInitialized Then Try ' 2. Llamamos al método delegado GetStats en el wrapper de HikariCP. Dim hikariStats As Map = MyHikariPool.GetStats Return hikariStats Catch ' Fallo en el método GetStats del wrapper. Dim ErrorMsg As String = $"RDCConnector.GetPoolStats: ERROR CRÍTICO al obtener estadísticas de HikariCP: ${LastException.Message}"$ Log(ErrorMsg) stats.Put("Error", LastException.Message) End Try Else stats.Put("Error", "Pool local MyHikariPool no inicializado.") End If Return stats End Sub ' *** NUEVA SUBRUTINA: Cierra el pool de conexiones de forma ordenada *** Public Sub Close If MyHikariPool.IsInitialized Then ' Cierre limpio del pool subyacente. MyHikariPool.ClosePool Log($"RDCConnector.Close: Pool Hikari cerrado limpiamente para este conector."$) End If ' Ya NO delegamos el cierre al Manager. ' ANTES: Main.ConnectionPoolManager1.ClosePoolByType(poolType) [15] End Sub ' --- SUBRUTINAS DE UTILIDAD PARA CARGA DE PROPIEDADES --- ' [2] Private Sub LoadDriverProperties(driverShortName As String, config_ As Map) driverProperties = ExtractProperties($"driver.${driverShortName.trim}."$, config_, Null, Null) End Sub ' [3] Private Sub ExtractProperties(prefix As String, input As Map, newPrefix As String, output As Map) As Map Dim properties As Map If output = Null Or output.IsInitialized = False Then properties.Initialize Else properties = output End If If newPrefix.EqualsIgnoreCase(Null) Then newPrefix = "" Dim prefixLength As Int = prefix.Length ' Log($"Prefijo=${prefix}, ${newPrefix}"$) For Each k As String In input.Keys ' Log(k) If k.ToLowerCase.StartsWith(prefix) Then ' Log($"found ${prefix}"$) Dim standardizedKey As String = k.SubString(prefixLength).ToLowerCase ' Log("Ponemos: " & $"${newPrefix}${k.SubString(prefixLength)}, ${input.Get(k)}"$) properties.Put($"${newPrefix}${standardizedKey}"$, input.Get(k)) End If Next Return properties End Sub Private Sub LoadPoolProperties(poolType As String, config_ As Map) ' Busca entradas como 'pool.hikari.' y las extrae. poolProperties = ExtractProperties($"pool.${poolType.ToLowerCase}."$, config_, Null, Null) End Sub