B4J Question B4jServer - ActiveConnections.Size

Declan

Well-Known Member
Licensed User
Longtime User
I am playing with B4jServer to setup a Webapp Dashboard app on my server.
For some reason I cannot get ActiveConnections.Size.
My Main Code:
B4X:
#Region Project Attributes
    #CommandLineArgs:
    #MergeLibraries: True
    
        'MySQL Connector/J Driver
    #AdditionalJar: mysql-connector-java-5.1.47-bin.jar
#End Region
Sub Process_Globals
    Public srvr As Server
    Public pool As ConnectionPool
    Private LiveTimer As Timer
    Public ActiveConnections As List ' Tracks active instances of DashboardWS
    
    Public SQL As SQL
    #Region Database Location
'    Private DBLocation As String = "localhost"
    Private DBUsername As String = "myUsername"
    Private DBPassword As String = "myPassword"
    
#End Region

    
End Sub

Sub AppStart (Args() As String)
    
    ActiveConnections.Initialize
    
    srvr.Initialize("srvr")
    srvr.Port = 5010
    
    
    ' Configure your MySQL connection
    ' Download driver from: https://mysql.com
    Dim driver As String = "com.mysql.cj.jdbc.Driver"
    
    Dim jdbcUrl As String = "jdbc:mysql://localhost:3306/my_db?useSSL=false"
    pool.Initialize(driver, jdbcUrl, DBUsername, DBPassword)

    ' Register the WebSocket handler for the dashboard page
    srvr.AddWebSocket("/dashboard/ws", "DashboardWS")
    
    ' Start background thread to monitor MySQL changes
    StartLiveMonitor
    
    srvr.Start
    StartMessageLoop
End Sub

Sub StartLiveMonitor
    ' Periodically queries MySQL and broadcasts updates to active browsers
    Dim t As Timer
    t.Initialize("LiveTimer", 10000) ' Poll every 10 seconds
    t.Enabled = True
End Sub

Sub LiveTimer_Tick
    Log("Timer Tick")
    ' Loop through all open browser sessions and push live database rows
    For i = ActiveConnections.Size - 1 To 0 Step -1
        Dim wsHandler As DashboardWS = ActiveConnections.Get(i)
        ' Use CallSub to safely invoke the class method across threads
        CallSub(wsHandler, "UpdateDashboardTable")
    Next
    
    Log("Connections: " & ActiveConnections.size)
    
End Sub

My Code in Websocket Class Module "DashboardWS":
B4X:
'WebSocket class
Sub Class_Globals
    Private ws As WebSocket
    Private xui As XUI
End Sub

Public Sub Initialize
    
End Sub

Private Sub WebSocket_Connected (WebSocket1 As WebSocket)
    ws = WebSocket1
    UpdateDashboardTable
End Sub

Private Sub WebSocket_Disconnected
    
End Sub

' Call this method whenever the timer detects new data in MySQL
Public Sub UpdateDashboardTable
    If ws.Open = False Then Return
    
    Dim sql As SQL = Main.pool.GetConnection
    Try
        Dim rs As ResultSet = sql.ExecQuery("SELECT * FROM avp.trans ORDER BY id DESC")
        Dim sb As StringBuilder
        sb.Initialize
        
        Do While rs.NextRow
            sb.Append("<tr>")
            sb.Append("<td>").Append(rs.GetString("id")).Append("</td>")
            sb.Append("<td>").Append(rs.GetString("deviceid")).Append("</td>")
            sb.Append("<td><span class='badge'>").Append(rs.GetString("msgtype")).Append("</span></td>")
            sb.Append("<td>").Append(rs.GetString("msgdate")).Append("</td>")
            sb.Append("</tr>")
            
            Log(rs.GetString("id"))
        Loop
        rs.Close
        
        ' Push the pure HTML rows directly into the table body element
        ws.RunFunction("UpdateTableBody", Array As Object(sb.ToString))
        ws.Flush
    Catch
        Log(LastException.Message)
        'Finally
        sql.Close
    End Try
End Sub
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
1.
B4X:
 ' Use CallSub to safely invoke the class method across threads
This is wrong. It must be CallSubDelayed for the code to be executed by the handler thread.

2. You need to add and remove the handler instances to ActiveConnections. It will not happen automatically. I recommend you to use a thread safe map for this with the thread id as the key.

B4X:
'main
Sub Process_Globals
 Private ActiveConnections As Map
 ....

Sub AppStart (Args() As String)
'...
ActiveConnections = srvr.CreateThreadSafeMap

Public Sub NewConnection(ws As Object)
 ActiveConnections.Put(srvr.CurrentThreadIndex, ws)
End Sub

Public Sub RemoveConnection()
 ActiveConnections.Remove(srvr.CurrentThreadIndex)
End Sub

'to call all handlers:
Log(ActiveConnections)
For Each ws As Object In ActiveConnections.Values
 CallSubDelayed (ws, "UpdateDashboardTable")
Next

You must call these methods from the handler:
B4X:
Private Sub WebSocket_Connected (WebSocket1 As WebSocket)
    ws = WebSocket1
    Main.NewConnection(Me)
    UpdateDashboardTable
End Sub

Private Sub WebSocket_Disconnected
    Main.RemoveConnection
End Sub
 
Upvote 0

Declan

Well-Known Member
Licensed User
Longtime User
Ok,
I have completed the changes to my code as per above.
However, I am still not getting the ActiveConnections.

My Code in Main:
B4X:
#Region Project Attributes
    #CommandLineArgs:
    #MergeLibraries: True
    
        'MySQL Connector/J Driver
    #AdditionalJar: mysql-connector-java-5.1.47-bin.jar
#End Region
Sub Process_Globals
    Public srvr As Server
    Public pool As ConnectionPool
    Private LiveTimer As Timer
'    Public ActiveConnections As List ' Tracks active instances of DashboardWS
    
    Private ActiveConnections As Map
    
    Public SQL As SQL
    #Region Database Location
'    Private DBLocation As String = "localhost"
    Private DBUsername As String = "root"
    Private DBPassword As String = "Declan2014!"
    
#End Region

    
End Sub

Sub AppStart (Args() As String)
    
    ActiveConnections = srvr.CreateThreadSafeMap
    
    ActiveConnections.Initialize
    
    srvr.Initialize("srvr")
    srvr.Port = 5010
    
    
    ' Configure your MySQL connection
    ' Download driver from: https://mysql.com
    Dim driver As String = "com.mysql.cj.jdbc.Driver"
    
    Dim jdbcUrl As String = "jdbc:mysql://localhost:3306/avp?useSSL=false"
    pool.Initialize(driver, jdbcUrl, DBUsername, DBPassword)

    ' Register the WebSocket handler for the dashboard page
    srvr.AddWebSocket("/dashboard/ws", "DashboardWS")
    
    ' Start background thread to monitor MySQL changes
    StartLiveMonitor
    
    srvr.Start
    StartMessageLoop
End Sub

Public Sub NewConnection(ws As Object)
    ActiveConnections.Put(srvr.CurrentThreadIndex, ws)
End Sub

Public Sub RemoveConnection()
    ActiveConnections.Remove(srvr.CurrentThreadIndex)
End Sub



Sub StartLiveMonitor
    ' Periodically queries MySQL and broadcasts updates to active browsers
    Dim t As Timer
    t.Initialize("LiveTimer", 10000) ' Poll every 10 seconds
    t.Enabled = True
End Sub

Sub LiveTimer_Tick
'    Log("Timer Tick")
    ' Loop through all open browser sessions and push live database rows
'    For i = ActiveConnections.Size - 1 To 0 Step -1
'        Dim wsHandler As DashboardWS = ActiveConnections.Get(i)
'        ' Use CallSub To safely invoke the class method across threads
'        CallSubDelayed(wsHandler, "UpdateDashboardTable")
'    Next
    'to call all handlers:
    Log("Active Connections: " & ActiveConnections)
    For Each ws As Object In ActiveConnections.Values
        CallSubDelayed (ws, "UpdateDashboardTable")
    Next
    
End Sub

My Code in Websocket Class Module "DashboardWS":
B4X:
'WebSocket class
Sub Class_Globals
    Private ws As WebSocket
    Private xui As XUI
End Sub

Public Sub Initialize
    
End Sub

Private Sub WebSocket_Connected (WebSocket1 As WebSocket)
    ws = WebSocket1
    Main.NewConnection(Me)
    UpdateDashboardTable
End Sub

Private Sub WebSocket_Disconnected
    Main.RemoveConnection
End Sub

' Call this method whenever the timer detects new data in MySQL
Public Sub UpdateDashboardTable
    If ws.Open = False Then Return
    
    Dim sql As SQL = Main.pool.GetConnection
    Try
        Dim rs As ResultSet = sql.ExecQuery("SELECT * FROM avp.trans ORDER BY id DESC")
        Dim sb As StringBuilder
        sb.Initialize
        
        Do While rs.NextRow
            sb.Append("<tr>")
            sb.Append("<td>").Append(rs.GetString("id")).Append("</td>")
            sb.Append("<td>").Append(rs.GetString("deviceid")).Append("</td>")
            sb.Append("<td>").Append(rs.GetString("msgtype")).Append("</td>")
            sb.Append("<td>").Append(rs.GetString("msgdate")).Append("</td>")
            sb.Append("</tr>")
            
            Log(rs.GetString("id"))
        Loop
        rs.Close
        
        ' Push the pure HTML rows directly into the table body element
        ws.RunFunction("UpdateTableBody", Array As Object(sb.ToString))
        ws.Flush
    Catch
        Log(LastException.Message)
        'Finally
        sql.Close
    End Try
End Sub

Running in debug, I see that NewConnection does not fire:
B4X:
Public Sub NewConnection(ws As Object)
    ActiveConnections.Put(srvr.CurrentThreadIndex, ws)
End Sub

Log:
B4X:
Waiting for debugger to connect...
Program started.
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
MLog initialization issue: slf4j found no binding or threatened to use its (dangerously silent) NOPLogger. We consider the slf4j library not found.
Jul 06, 2026 3:46:33 PM com.mchange.v2.log.MLog
INFO: MLog clients using java 1.4+ standard logging.
Jul 06, 2026 3:46:34 PM com.mchange.v2.c3p0.C3P0Registry
INFO: Initializing c3p0-0.9.5.2 [built 08-December-2015 22:06:04 -0800; debug? true; trace: 10]
Emulated network latency: 100ms
Active Connections: (MyMap) {}
Active Connections: (MyMap) {}
Active Connections: (MyMap) {}

When I run the app on the server and connect with
It loads, but does not access my MySQL database.
 

Attachments

  • Dashboard-Test.png
    Dashboard-Test.png
    22.6 KB · Views: 17
Upvote 0

Erel

B4X founder
Staff member
Licensed User
Longtime User
Correct (but not related to any other issue):
B4X:
 ActiveConnections = srvr.CreateThreadSafeMap
No need to initialize the variable if you assign a different object later.


There is a bug here (that is also not related to any other issue):
B4X:
 Catch
        Log(LastException.Message)
        'Finally
        sql.Close
    End Try
sql.Close should be called after the "End Try" line or it will not be closed when there is no error.
 
Upvote 0

Declan

Well-Known Member
Licensed User
Longtime User
Catch Log(LastException.Message) 'Finally sql.Close End Try
Thanks
B4X:
    Catch
        Log(LastException.Message)
    End Try
    sql.Close

I still cannot understand why I am unable to get the Activeconnections
I have a serious issue that I cannot see
 
Upvote 0

teddybear

Well-Known Member
Licensed User
I still cannot understand why I am unable to get the Activeconnections
I have a serious issue that I cannot see
The code works in post #2.
You'd better upload a small project for this.
Obviously, your client did not call the DashboardWS at all.
Note that in debugging mode, the maximum size of Activeconnections is always 1.
 
Upvote 0

alwaysbusy

Expert
Licensed User
Longtime User
This is the minimum you need.

You look at the html to connect to the websocket.

I would use a GUID instead of srvr.CurrentThreadIndex because I think in debug it run in single tread modus (in the main).
 

Attachments

  • DashboardWS.zip
    4.8 KB · Views: 10
Last edited:
Upvote 0

teddybear

Well-Known Member
Licensed User
Obviously, there are issues with your index.html
B4X:
src="https://jquery.com"

b4j_connect("/ws/dashboard");

it should be like this

B4X:
src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.js"

b4j_connect("/dashboard/ws");
 

Attachments

  • index.7z
    1.1 KB · Views: 10
Upvote 0

Declan

Well-Known Member
Licensed User
Longtime User
Running this index.html, I get the attached when login in.
Still not receiving Activeconnections

Log:
B4X:
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
MLog initialization issue: slf4j found no binding or threatened to use its (dangerously silent) NOPLogger. We consider the slf4j library not found.
Jul 07, 2026 10:44:20 AM com.mchange.v2.log.MLog
INFO: MLog clients using java 1.4+ standard logging.
Jul 07, 2026 10:44:20 AM com.mchange.v2.c3p0.C3P0Registry
INFO: Initializing c3p0-0.9.5.2 [built 08-December-2015 22:06:04 -0800; debug? true; trace: 10]
Active Connections: (ConcurrentMyMap) {}
Active Connections: (ConcurrentMyMap) {}
 

Attachments

  • Dash-03.png
    Dash-03.png
    58.8 KB · Views: 6
Upvote 0

Declan

Well-Known Member
Licensed User
Longtime User
This is the minimum you need.

You look at the html to connect to the websocket.

I would use a GUID instead of srvr.CurrentThreadIndex because I think in debug it run in single tread modus (in the main).
@alwaysbusy
Running the ZIP file app, I receive the attached when login.

My Log:
B4X:
2026-07-07 11:08:41.257:INFO :oejs.Server:main: jetty-11.0.9; built: 2022-03-30T17:44:47.085Z; git: 243a48a658a183130a8c8de353178d154ca04f04; jvm 19.0.2+7-44
2026-07-07 11:08:41.415:INFO :oejss.DefaultSessionIdManager:main: Session workerName=node0
2026-07-07 11:08:41.450:INFO :oejsh.ContextHandler:main: Started o.e.j.s.ServletContextHandler@1df82230{/,file:///C:/Users/RobertC/Desktop/DashboardWS/Objects/www/,AVAILABLE}
2026-07-07 11:08:41.513:INFO :oejs.RequestLogWriter:main: Opened C:\Users\RobertC\Desktop\DashboardWS\Objects\logs\b4j-2026_07_07.request.log
2026-07-07 11:08:41.621:INFO :oejs.AbstractConnector:main: Started ServerConnector@3ac3fd8b{HTTP/1.1, (http/1.1)}{0.0.0.0:5010}
2026-07-07 11:08:41.642:INFO :oejs.Server:main: Started Server@61f8bee4{STARTING}[11.0.9,sto=0] @853ms
Active Connections: 0
Active Connections: 0
Active Connections: 0
Active Connections: 0
Seems like some progress?
 

Attachments

  • Dash-04.png
    Dash-04.png
    22.6 KB · Views: 8
Upvote 0

Declan

Well-Known Member
Licensed User
Longtime User
open several tabs in your browser to http://127.0.0.1:5010/dashboardws/index.html and I got an active connection for each open tab.
Many Thanks, This works
Attached is edited version of Post #10 with current index.html
Kindly check to see if all OK.

Log:
B4X:
2026-07-07 13:34:49.435:INFO :oejs.Server:main: jetty-11.0.9; built: 2022-03-30T17:44:47.085Z; git: 243a48a658a183130a8c8de353178d154ca04f04; jvm 19.0.2+7-44
2026-07-07 13:34:49.580:INFO :oejss.DefaultSessionIdManager:main: Session workerName=node0
2026-07-07 13:34:49.615:INFO :oejsh.ContextHandler:main: Started o.e.j.s.ServletContextHandler@1df82230{/,file:///C:/Users/RobertC/Desktop/DashboardWS/Objects/www/,AVAILABLE}
2026-07-07 13:34:49.678:INFO :oejs.RequestLogWriter:main: Opened C:\Users\RobertC\Desktop\DashboardWS\Objects\logs\b4j-2026_07_07.request.log
2026-07-07 13:34:49.790:INFO :oejs.AbstractConnector:main: Started ServerConnector@3ac3fd8b{HTTP/1.1, (http/1.1)}{0.0.0.0:5010}
2026-07-07 13:34:49.808:INFO :oejs.Server:main: Started Server@61f8bee4{STARTING}[11.0.9,sto=0] @816ms
New one connected: ebf5eab8-04da-4390-be00-04ca0f13a46c
Active Connections: 1
Disconnected: ebf5eab8-04da-4390-be00-04ca0f13a46c
New one connected: eb3938d5-2571-4df0-8a86-27d91e541780
Active Connections: 1
New one connected: ce33dc8b-769e-4f06-b2a5-db7272c8082e
Active Connections: 2
Disconnected: eb3938d5-2571-4df0-8a86-27d91e541780
Active Connections: 1
Active Connections: 1

Now to fathom how to populate dashboard table and combobox with MySQL data
 

Attachments

  • ActiveConnections.zip
    33.9 KB · Views: 7
Upvote 0
Top