B4J=true Group=Default Group ModulesStructureVersion=1 Type=Class Version=6.01 @EndOfDesignText@ 'Class module : HikariConnectionPool '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) ' Optimal default values for HikariCP (in milliseconds) 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 ' Temporary variable for the calculated maximum size ' 1. MaxLifetime (Long) - Key: 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 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 ' Store the config value ' 2. ConnectionTimeout (Long) - Key: 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 ConnectionTimeout: ${connTimeout}"$) Else connTimeout = DEFAULT_CONN_TIMEOUT poolJO.RunMethod("setConnectionTimeout", Array As Object(connTimeout)) Log($"Ponemos ConnectionTimeout: ${DEFAULT_CONN_TIMEOUT}"$) End If processedKeys.Add("connectiontimeout") ConnTimeoutConfig = connTimeout ' Store the config value ' 3. LeakDetectionThreshold (Long) - Key: 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 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 ' Store the config value ' 4. MaximumPoolSize (Int) - Key: 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 MaximumPoolSize: ${maxSize}"$) Else maxSize = DEFAULT_MAX_SIZE poolJO.RunMethod("setMaximumPoolSize", Array As Object(DEFAULT_MAX_SIZE)) Log($"Ponemos MaximumPoolSize: ${DEFAULT_MAX_SIZE}"$) End If processedKeys.Add("maximumpoolsize") PoolSizeConfig = maxSize ' Store the config value ' 5. MinimumIdle (Int) - Key: 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 MinimumIdle: ${minIdleFinal}"$) Else ' APPLY FIXED POOL AXIOM: MinimumIdle = MaximumPoolSize (maxSize) minIdleFinal = maxSize poolJO.RunMethod("setMinimumIdle", Array As Object(minIdleFinal)) Log($"Ponemos MinimumIdle: ${minIdleFinal} (Igual a MaximumPoolSize)"$) End If processedKeys.Add("minimumidle") MinIdleConfig = minIdleFinal ' Store the config value ' 6. RegisterMbeans (Boolean) - Key: registermbeans If properties.ContainsKey("registermbeans") Then Dim regMbeans As Boolean = properties.Get("registermbeans") poolJO.RunMethod("setRegisterMbeans", Array As Object(regMbeans)) Log($"Ponemos RegisterMbeans: ${regMbeans}"$) Else poolJO.RunMethod("setRegisterMbeans", Array As Object(DEFAULT_REG_MBEANS)) Log($"Ponemos RegisterMbeans: ${DEFAULT_REG_MBEANS}"$) End If processedKeys.Add("registermbeans") ' 7. KeepaliveTime (Long) - Key: keepalivetime Dim keepAlive As Long If properties.ContainsKey("keepalivetime") Then rawValue = properties.Get("keepalivetime").As(String).Trim keepAlive = rawValue ' The minimum accepted value is 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 KeepaliveTime: ${DEFAULT_KEEP_ALIVE_TIME} (50 minutes)"$) End If processedKeys.Add("keepalivetime") KeepAliveTimeConfig = keepAlive ' Store the config value ' Process remaining properties 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 (Remaining): ${k}, ${properties.Get(k)}"$) Catch Log($"Warning (Remaining): 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 ' Applies specific JDBC Driver properties (e.g., MySQL caching) ' These properties are applied using addDataSourceProperty, which is not supported in standard SetProperties. Public Sub SetDriverProperties(properties As Map) ' properties is the map extracted by RDCConnector.LoadDriverProperties (e.g., driver.mysql.*). Dim value As Object For Each k As String In properties.Keys value = properties.Get(k) ' Use addDataSourceProperty to configure the Driver, not the 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 ' WARNING: This subroutine translates internal HikariCP method names ' to the generic keys (e.g., BusyConnections) that RDCConnector expects. ' It now includes the STATIC CONFIGURATION saved in the class variables. Public Sub GetStats As Map Dim stats As Map stats.Initialize ' 1. ADD STATIC PROPERTIES (STORED CONFIGURATION) stats.Put("MaxPoolSize", PoolSizeConfig) stats.Put("MinPoolSize", MinIdleConfig) stats.Put("MaxLifetime", MaxLifeConfig) stats.Put("ConnectionTimeout", ConnTimeoutConfig) stats.Put("LeakDetectionThreshold", LeakDetectionThresholdConfig) stats.Put("KeepaliveTime", KeepAliveTimeConfig) ' Note: You can add other relevant static properties here. ' 2. GET RUNTIME METRICS (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 getting dynamic metrics from MBean: " & LastException.Message Log(ErrorMsg) stats.Put("Error_Runtime", ErrorMsg) End Try Else stats.Put("Error", "Pool JO not initialized") End If Return stats End Sub