B4J Question [Server] Wait For a SQLite data to change

avalle

Active Member
Licensed User
Longtime User
Hi,
I understand how to use Wait For in a B4J Server Handler. However I have no idea how to pause a server handler class waiting for some data in a SQLite database to change.

Anyone so kind to help?

Thanks
Andrea
 
Last edited:

OliverA

Expert
Licensed User
Longtime User
Don't use the async methods? Since each server request creates a new Server Hanlder, not using the async methods does not impact other requests (since those are threaded).
 
Upvote 0

OliverA

Expert
Licensed User
Longtime User
Then what is your issue? I guess provide some code and explain what you are needing.
 
Upvote 0

avalle

Active Member
Licensed User
Longtime User
Here's some code...

B4X:
Sub Handle(req As ServletRequest, resp As ServletResponse)
    CheckUser(resp)
    StartMessageLoop
End Sub

Sub CheckUser(resp as ServletResponse)
    Dim cursor As ResultSet
    cursor = sqlObj.ExecQuery($"SELECT * FROM Auth WHERE SessionID="${sessionID}""$)
    AuthCallback(cursor, login_hint)

    Wait For Callback_Done
    cursor.Close
    resp.SendRedirect($"${redirect_uri}?session=sessionID"$)
    StopMessageLoop
End Sub

Sub AuthCallback(cursor As ResultSet, login_hint As String)
    Do While (True)
        Sleep(1000)
        login_hint = cursor.GetString("LoginHint")
        If (login_hint <> "") Then Exit
    Loop
    CallSubDelayed(Me, "Callback_Done")
End Sub

My issue is that I don't have an event to Wait For while searching the DB for the new data to become available.
So I created a resumable sub with a custom event (Callback_Done) that should be generated by the AuthCallback routine when valid data is available in the DB.

Another handler in the same server is responsible for getting some user data and write the "LoginHint" field into the DB.
This code apparently works, however the other handler becomes unresponsive: the execution never leaves the handler with the code above.

Hope it helps understand what I'm trying to achieve.
 
Upvote 0

OliverA

Expert
Licensed User
Longtime User
As your code stands, you are creating an endless loop in AuthCallback. GetString will always return the same result, so if it returns “”, you’ll have an endless loop. When your ExecQuery is called, it does not return until the query is complete. Therefore, cursor will contain the result. No need to create a waiting loop
 
Upvote 0

avalle

Active Member
Licensed User
Longtime User
Sorry you're right I need to read again the cursor in the loop. I've fixed that.

However that is not the problem.
The problem is that the code above never lets the other handler to execute, despite the Sleep in the While loop.
So my DB never receives the new data from the user so that LoginHint can be <> ""
 
Upvote 0

OliverA

Expert
Licensed User
Longtime User
This works
B4X:
'Handler class
Sub Class_Globals
   
End Sub

Public Sub Initialize
   
End Sub

Sub Handle(req As ServletRequest, resp As ServletResponse)
   Log("Handle: You have arrived!")
   CheckUser(resp)
   StartMessageLoop
   Log("Handle: Exiting")
End Sub

Sub CheckUser(resp As ServletResponse)
   Log("CheckUser: Entering")
   Dim cursor As String
   cursor = ""
   Log("CheckUser: Calling AuthCallback")
   AuthCallback(cursor, "")
   Log("CheckUser: Waiting on Callback_Done")
   Wait For Callback_Done
   Log("CheckUser: Callback_Done was called")
   resp.Write("Howdy user!")
   Log("CheckUser: Stopping the message loop")
   StopMessageLoop
End Sub

Sub AuthCallback(cursor As String, login_hint As String)
   Log("AuthCallback: Entering")
   Dim x As Int = 0
   Do While True
       Sleep(1000)
       x = x + 1
       If x = 5 Then
           login_hint = "This is a hint"
       End If
       If login_hint <> "" Then Exit
       Log($"AuthCallback: login_hint is still """$)
   Loop
   Log($"AuthCallback: Queuing the callback..."$)
   CallSubDelayed(Me, "Callback_Done")
   Log("AuthCallback: Exiting")
End Sub
You really need to log your process. I still think that the issue is with your Do While (True) loop. You just assume that the login_hint is "" (at least the code you give does). You also assume that your query will return a non "" login_hint. What makes you think so? What would suddenly change in your DB that from one call to the other, the login_hint changes from "" to something else? Log your loop and proof to yourself that login_hint is not "".
 
Upvote 0

avalle

Active Member
Licensed User
Longtime User
I really appreciate your help Oliver.
Let me add a few more details:
1. The B4J app is a server app implementing REST services. In addition to the Server Handler above there are many other handlers associated with multiple AddHandler definitions in the Main code:

B4X:
Sub AppStart (Args() As String)
    Private srvr As Server
    srvr.Initialize("srvr")
    srvr.AddHandler("/authorization/login", "login", False)
    srvr.AddHandler("/authorization/register", "register", False)
End Sub

2. The DB includes the Auth table, with sessionID and LoginHint fields among others.

3. The "login" handler class contains the code I shared before, and it works fine as in your case.
The "register" handler class instead contains the following code:

B4X:
Sub Handle(req As ServletRequest, resp As ServletResponse)
    Dim parser As JSONParser
    parser.Initialize(json)
    Dim root As Map = parser.NextObject
    Dim login_hint As String = root.Get("login_hint")
    Log ("login_hint: " & login_hint)
    
    Dim cursor As ResultSet
    cursor = Main.sqlObj.ExecQuery($"SELECT * FROM NDIAuth WHERE AuthReqID="${auth_req_id}""$)
    sqlObj.ExecNonQuery($"UPDATE Auth SET LoginHint="${login_hint}" WHERE sessionID="${sessionID}""$)
    resp.Write("")
End Sub

4. If I run the server and call the /authorization/register endpoint I can update the DB and write the LoginHint value, that is initially empty.
If I call the /authorization/login endpoint the code in AuthCallback should loop until the "register" handler writes a value in the LoginHint field of the sessionID record.
Instead, when AuthCallback is looping the "register" Handler becomes unresponsive, so if the user calls the /authorization/register the server does not respond and therefore LoginHint would never be updated and therefore the AuthCallback loop would never exit.

Hope this helps... Thanks again
 
Upvote 0

OliverA

Expert
Licensed User
Longtime User
Did you enable WAL mode on your SQLite database? You only use one SQL object for all your SQLite DB access, correct?
 
Upvote 0

avalle

Active Member
Licensed User
Longtime User
Yes, but again that's not the problem. Access to the DB is ok.
It's the access to the server handlers that it's not concurrent as expected.

According to the B4J server tutorial a multithreaded handler means that your handler code will be run by a thread from the server threads pool. Multiple instances of the same handler and other handlers can be executed in the same time.
 
Upvote 0

avalle

Active Member
Licensed User
Longtime User
However now I notice that the tutorial also says:
Note that when you run your code in Debug mode the handlers will always run in the main thread.

I should have probably mentioned that, but I was testing my code in Debug mode.
And in fact if I switch to Release mode it works nicely...

Not ideal, but at least I have a working starting point.
Thanks again to @OliverA for helping out.
 
Upvote 0
Top