B4J=true Group=Default Group ModulesStructureVersion=1 Type=Class Version=6.01 @EndOfDesignText@ 'Class module 'Author: Oliver Ackermann 'Created on: 2018/05/07 'Based on: https://github.com/AnywhereSoftware/B4J_Server/blob/master/src/anywheresoftware/b4j/object/ConnectionPool.java 'Resources: ' https://github.com/brettwooldridge/HikariCP ' addDataSourceProperty - individual driver property ' setDataSourceProperties - Java Property values Sub Class_Globals Private poolJO As JavaObject Private poolProperties As Map Private PoolSizeConfig As Int Private MinIdleConfig As Int Private MaxLifeConfig As Long Private ConnTimeoutConfig As Long Private LeakDetectionThresholdConfig As Long Private KeepAliveTimeConfig As Long End Sub Public Sub Initialize Dim properties As Map properties.Initialize ' 'Frequently used properties.Put("autocommit", "boolean") properties.Put("connectiontimeout", "int") properties.Put("idletimeout", "int") properties.Put("maxlifetime", "int") properties.Put("connectiontestquery", "string") properties.Put("minimumidle", "int") properties.Put("maximumpoolsize", "int") properties.Put("poolname", "string") properties.Put("keepalivetime", "int") 'Infrequently used properties.Put("initializationfailtimeout", "int") properties.Put("isolateinternalqueries", "boolean") properties.Put("allowpoolsuspension", "boolean") properties.Put("readonly", "boolean") properties.Put("registermbeans", "boolean") properties.Put("connectioninitsql", "string") properties.Put("validationtimeout", "int") properties.Put("leakdetectionthreshold", "int") poolProperties = properties End Sub 'Creates the pool. ' driverClass - The JDBC driver class. ' jdbcUrl - JDBC connection url. ' aUser / aPassword - Connection credentials. Public Sub CreatePool(driverClass As String, jdbcUrl As String, aUser As String, aPassword As String) CreatePool2(driverClass, jdbcUrl, aUser, aPassword, 0) End Sub 'Creates the pool. ' driverClass - The JDBC driver class. ' jdbcUrl - JDBC connection url. ' aUser / aPassword - Connection credentials. ' poolSize - Maximum size of connection pool. Pool's default used when < 1. Public Sub CreatePool2(driverClass As String, jdbcUrl As String, aUser As String, aPassword As String, poolSize As Int) Dim jo As JavaObject poolJO = jo.InitializeNewInstance("com.zaxxer.hikari.HikariDataSource", Null) poolJO.RunMethod("setDriverClassName",Array As Object(driverClass)) poolJO.RunMethod("setJdbcUrl", Array As Object(jdbcUrl)) poolJO.RunMethod("setUsername", Array As Object(aUser)) poolJO.RunMethod("setPassword", Array As Object(aPassword)) If poolSize > 0 Then poolJO.RunMethod("setMaximumPoolSize", Array As Object(poolSize)) End Sub Public Sub CreatePool3(options As Map) CreatePool2(options.Get("driver"), options.Get("url"), options.Get("user"), options.Get("password"), options.Get("poolsize")) End Sub 'https://github.com/openbouquet/HikariCP/blob/master/src/main/java/com/zaxxer/hikari/HikariConfig.java 'HikariConfig can use Properties object. But this must be done before creating the pool. The pool than is created using 'that HikariConfig object. Public Sub SetProperties(properties As Map) ' Valores óptimos por defecto para HikariCP (en milisegundos) Private const DEFAULT_MAX_LIFE As Long = 1750000 Private const DEFAULT_CONN_TIMEOUT As Long = 30000 Private const DEFAULT_LEAK_THR As Long = 35000 Private const DEFAULT_REG_MBEANS As Boolean = True Private const DEFAULT_MIN_IDLE As Int = 10 Private const DEFAULT_MAX_SIZE As Int = 10 Private const DEFAULT_KEEP_ALIVE_TIME As Long = 300000 Dim rawValue As String Dim processedKeys As List processedKeys.Initialize Dim maxSize As Int ' Variable temporal para el tamaño máximo calculado ' --- INICIO: Procesamiento de Propiedades Críticas --- ' 1. MaxLifetime (Long) - Clave: maxlifetime Dim maxLife As Long If properties.ContainsKey("maxlifetime") Then rawValue = properties.Get("maxlifetime").As(String).Trim maxLife = rawValue poolJO.RunMethod("setMaxLifetime", Array As Object(maxLife)) Log($"Ponemos (LONG, Config): MaxLifetime, ${maxLife}"$) Else maxLife = DEFAULT_MAX_LIFE poolJO.RunMethod("setMaxLifetime", Array As Object(maxLife)) Log($"Ponemos (LONG, Default): MaxLifetime, ${DEFAULT_MAX_LIFE}"$) End If processedKeys.Add("maxlifetime") MaxLifeConfig = maxLife ' <-- ALMACENAMIENTO CRÍTICO ' 2. ConnectionTimeout (Long) - Clave: connectiontimeout Dim connTimeout As Long If properties.ContainsKey("connectiontimeout") Then rawValue = properties.Get("connectiontimeout").As(String).Trim connTimeout = rawValue poolJO.RunMethod("setConnectionTimeout", Array As Object(connTimeout)) Log($"Ponemos (LONG, Config): ConnectionTimeout, ${connTimeout}"$) Else connTimeout = DEFAULT_CONN_TIMEOUT poolJO.RunMethod("setConnectionTimeout", Array As Object(connTimeout)) Log($"Ponemos (LONG, Default): ConnectionTimeout, ${DEFAULT_CONN_TIMEOUT}"$) End If processedKeys.Add("connectiontimeout") ConnTimeoutConfig = connTimeout ' <-- ALMACENAMIENTO CRÍTICO ' 3. LeakDetectionThreshold (Long) - Clave: leakdetectionthreshold Dim leakThr As Long If properties.ContainsKey("leakdetectionthreshold") Then rawValue = properties.Get("leakdetectionthreshold").As(String).Trim leakThr = rawValue poolJO.RunMethod("setLeakDetectionThreshold", Array As Object(leakThr)) Log($"Ponemos (LONG, Config): LeakDetectionThreshold, ${leakThr}"$) Else leakThr = DEFAULT_LEAK_THR poolJO.RunMethod("setLeakDetectionThreshold", Array As Object(leakThr)) Log($"Ponemos (LONG, Default): LeakDetectionThreshold, ${DEFAULT_LEAK_THR}"$) End If processedKeys.Add("leakdetectionthreshold") LeakDetectionThresholdConfig = leakThr ' <-- ALMACENAMIENTO CRÍTICO ' ********** LÓGICA DE FIXED POOL: MaximumPoolSize primero ********** ' 4. MaximumPoolSize (Int) - Clave: maximumpoolsize If properties.ContainsKey("maximumpoolsize") Then rawValue = properties.Get("maximumpoolsize").As(String).Trim maxSize = rawValue.As(Int) poolJO.RunMethod("setMaximumPoolSize", Array As Object(maxSize)) Log($"Ponemos (INT, Config): MaximumPoolSize, ${maxSize}"$) Else maxSize = DEFAULT_MAX_SIZE poolJO.RunMethod("setMaximumPoolSize", Array As Object(DEFAULT_MAX_SIZE)) Log($"Ponemos (INT, Default): MaximumPoolSize, ${DEFAULT_MAX_SIZE}"$) End If processedKeys.Add("maximumpoolsize") PoolSizeConfig = maxSize ' <-- ALMACENAMIENTO CRÍTICO ' 5. MinimumIdle (Int) - Clave: minimumidle Dim minIdleFinal As Int If properties.ContainsKey("minimumidle") Then rawValue = properties.Get("minimumidle").As(String).Trim minIdleFinal = rawValue.As(Int) poolJO.RunMethod("setMinimumIdle", Array As Object(minIdleFinal)) Log($"Ponemos (INT, Config): MinimumIdle, ${minIdleFinal}"$) Else ' APLICAMOS EL FIXED POOL AXIOM: MinimumIdle = MaximumPoolSize (maxSize) minIdleFinal = maxSize poolJO.RunMethod("setMinimumIdle", Array As Object(minIdleFinal)) Log($"Ponemos (INT, Fixed Default): MinimumIdle, ${minIdleFinal} (Igual a MaximumPoolSize)"$) End If processedKeys.Add("minimumidle") MinIdleConfig = minIdleFinal ' <-- ALMACENAMIENTO CRÍTICO ' ********** FIN DE LA LÓGICA DE FIXED POOL ********** ' 6. RegisterMbeans (Boolean) - Clave: registermbeans If properties.ContainsKey("registermbeans") Then Dim regMbeans As Boolean = properties.Get("registermbeans") poolJO.RunMethod("setRegisterMbeans", Array As Object(regMbeans)) Log($"Ponemos (BOOL, Config): RegisterMbeans, ${regMbeans}"$) Else poolJO.RunMethod("setRegisterMbeans", Array As Object(DEFAULT_REG_MBEANS)) Log($"Ponemos (BOOL, Default): RegisterMbeans, ${DEFAULT_REG_MBEANS}"$) End If processedKeys.Add("registermbeans") ' 7. KeepaliveTime (Long) - Clave: keepalivetime Dim keepAlive As Long If properties.ContainsKey("keepalivetime") Then rawValue = properties.Get("keepalivetime").As(String).Trim keepAlive = rawValue ' El valor mínimo aceptado es 30,000 ms [4]. If keepAlive < 30000 Then keepAlive = 30000 poolJO.RunMethod("setKeepaliveTime", Array As Object(keepAlive)) Log($"Ponemos (LONG, Config): KeepaliveTime, ${keepAlive}"$) Else keepAlive = DEFAULT_KEEP_ALIVE_TIME poolJO.RunMethod("setKeepaliveTime", Array As Object(keepAlive)) Log($"Ponemos (LONG, Default): KeepaliveTime, ${DEFAULT_KEEP_ALIVE_TIME} (50 minutos)"$) End If processedKeys.Add("keepalivetime") KeepAliveTimeConfig = keepAlive ' <-- ALMACENAMIENTO CRÍTICO ' --- INICIO: PROCESAMIENTO DE PROPIEDADES RESTANTES --- ' ... (El resto del código de procesamiento de propiedades restantes se mantiene igual) Dim intValue As Int Dim booleanValue As Boolean Dim variableType As String For Each k As String In properties.Keys If processedKeys.IndexOf(k) = -1 Then If poolProperties.ContainsKey(k) Then variableType = poolProperties.Get(k) Dim valueToUse As String = properties.Get(k) If variableType = "string" Then valueToUse = valueToUse.Trim End If Dim dynamicMethodName As String = $"set${k}"$ Try If variableType = "int" Then intValue = valueToUse poolJO.RunMethod(dynamicMethodName, Array As Object(intValue)) Else If variableType = "string" Then poolJO.RunMethod(dynamicMethodName, Array As Object(valueToUse)) Else If variableType = "boolean" Then booleanValue = valueToUse GetPoolJO.RunMethod(dynamicMethodName, Array As Object(booleanValue)) Else Log($"Connection pool property ${k} has unsupported variable type of ${variableType}"$) End If Log($"Ponemos (Restante): ${k}, ${properties.Get(k)}"$) Catch Log($"Warning (Restante): Method: ${dynamicMethodName} not matched or type mismatch for property ${k}. Error: ${LastException.Message}"$) End Try Else Log($"Warning: Property ${k} not supported"$) End If End If Next End Sub ' // --------------------------------------------------------------------------------------------------------------------------------------------------- ' // NUEVA SUBRUTINA: Aplica propiedades específicas del Driver JDBC (Ej: MySQL caching) ' // Estas propiedades se aplican usando addDataSourceProperty, que no se soporta en SetProperties estándar. ' // --------------------------------------------------------------------------------------------------------------------------------------------------- Public Sub SetDriverProperties(properties As Map) ' properties es el mapa que RDCConnector.LoadDriverProperties extrae (ej: driver.mysql.*). Dim value As Object For Each k As String In properties.Keys value = properties.Get(k) ' CRÍTICO: Usar addDataSourceProperty para configurar el Driver, no el Pool. poolJO.RunMethod("addDataSourceProperty", Array As Object(k, value)) Log($"HikariCP: Driver Property [${k}] added with value: ${value}"$) Next End Sub 'Check if JDBC URL is supported Public Sub SupportUrl(jdbcUrl As String) As Boolean If jdbcUrl.StartsWith("jdbc:sqlite") Then Log("Error: Hikari connection pool does not support SQLite") Return False End If Return True End Sub 'See if pool's java object supports standard getConnection method Public Sub IsStandardConnection As Boolean Return True End Sub 'Get pool's underlying java object. Used if IsStandardConnection is True Public Sub GetPoolJO As JavaObject Return poolJO End Sub 'Retrieves a connection from the pool. Make sure to close the connection when you are done with it. Public Sub GetConnection As SQL Dim newSQL As SQL Dim jo As JavaObject = newSQL jo.SetField("connection", poolJO.RunMethod("getConnection", Null)) Return newSQL End Sub 'Closes all unused pooled connections. Public Sub ClosePool If poolJO.IsInitialized Then poolJO.RunMethod("close", Null) End If End Sub ' ** ADVERTENCIA: Esta subrutina traduce los nombres de métodos internos de HikariCP ' ** a las claves genéricas (ej. BusyConnections) que RDCConnector espera. ' ** CRÍTICO: Ahora incluye la CONFIGURACIÓN ESTÁTICA guardada en las variables de clase. Public Sub GetStats As Map Dim stats As Map stats.Initialize ' 1. AGREGAR PROPIEDADES ESTÁTICAS (CONFIGURACIÓN ALMACENADA) stats.Put("MaxPoolSize", PoolSizeConfig) stats.Put("MinPoolSize", MinIdleConfig) stats.Put("MaxLifetime", MaxLifeConfig) stats.Put("ConnectionTimeout", ConnTimeoutConfig) stats.Put("LeakDetectionThreshold", LeakDetectionThresholdConfig) stats.Put("KeepaliveTime", KeepAliveTimeConfig) ' Nota: Aquí puedes agregar las propiedades estáticas adicionales que sean relevantes. ' 2. OBTENER MÉTRICAS DE TIEMPO DE EJECUCIÓN (JMX/MBean) If poolJO.IsInitialized Then Try Dim poolMBean As JavaObject = poolJO.RunMethod("getHikariPoolMXBean", Null) Dim busyConn As Object = poolMBean.RunMethod("getActiveConnections", Null) stats.Put("BusyConnections", busyConn) Dim idleConn As Object = poolMBean.RunMethod("getIdleConnections", Null) stats.Put("IdleConnections", idleConn) Dim activeCount As Int = busyConn Dim idleCount As Int = idleConn stats.Put("TotalConnections", activeCount + idleCount) Dim awaitingConn As Object = poolMBean.RunMethod("getThreadsAwaitingConnection", Null) stats.Put("ThreadsAwaitingConnection", awaitingConn) Catch Dim ErrorMsg As String = "HikariCP.GetStats: Error al obtener métricas dinámicas del MBean: " & LastException.Message Log(ErrorMsg) stats.Put("Error_Runtime", ErrorMsg) End Try Else stats.Put("Error", "Pool JO no inicializado") End If Return stats End Sub