B4J Question DBRequestManager HandleJobAsync's and "unprocessed" Wait For's

OliverA

Expert
Licensed User
Longtime User
Currently DBRequestManager's HandleJobAsync code is as follows
B4X:
Public Sub HandleJobAsync(Job As HttpJob, EventName As String)
   Dim ser As B4XSerializator
   Dim data() As Byte = Bit.InputStreamToBytes(Job.GetInputStream)
   ser.ConvertBytesToObjectAsync(data, "ser")
   Wait For (ser) ser_BytesToObject (Success As Boolean, NewObject As Object)
   If Success = False Then
       Log("Error reading response: " & LastException)
       Return
   End If
   Dim res As DBResult = NewObject
   res.Tag = Job.Tag
   CallSubDelayed2(mTarget, EventName & "_result", res)
End Sub
A typical use for this method is shown in the example provided in the jRDC2 page ():
B4X:
Sub GetRecord (id As Int)
   Dim req As DBRequestManager = CreateRequest
   Dim cmd As DBCommand = CreateCommand("select_animal", Array(id))
   Wait For (req.ExecuteQuery(cmd, 0, Null)) JobDone(j As HttpJob)
   If j.Success Then
       req.HandleJobAsync(j, "req")
       Wait For (req) req_Result(res As DBResult)
       'work with result
       req.PrintTable(res)
   Else
       Log("ERROR: " & j.ErrorMessage)
   End If
   j.Release
End Sub
The issue I encounter is that if in HandleJobAsync, Success = False, then the Wait For in GetRecord never "finishes".
Modified GetRecord routine:
B4X:
Sub GetRecord2 (id As Int)
   Log($"GetRecord2 call # ${id}: entry"$)
   Dim req As DBRequestManager = CreateRequest
   Dim cmd As DBCommand = CreateCommand("select_studentids", Array())
   Wait For (req.ExecuteQuery(cmd, 0, Null)) JobDone(j As HttpJob)
   If j.Success Then
       Log($"GetRecord2 call # ${id}: Successful query. Retrieving data"$)
       req.HandleJobAsync(j, "req")
       Log($"GetRecord2 call # ${id}: Before Wait For"$)
       Wait For (req) req_Result(res As DBResult)
       Log($"GetRecord2 call # ${id}: After Wait For"$)
       'work with result
       'req.PrintTable(res)
   Else
       'Log("ERROR: " & j.ErrorMessage)
       Log($"GetRecord2 call # ${id}: ERROR: ${j.ErrorMessage}"$)
   End If
   j.Release
   Log($"GetRecord2 call # ${id}: exit"$)
End Sub
Called with following loop
B4X:
For i = 0 To 3
   GetRecord2(i)
Next
will produce the following result when everything goes ok
Waiting for debugger to connect...
Program started.
GetRecord2 call # 0: entry
GetRecord2 call # 1: entry
GetRecord2 call # 0: Successful query. Retrieving data
GetRecord2 call # 0: Before Wait For
GetRecord2 call # 1: Successful query. Retrieving data
GetRecord2 call # 1: Before Wait For
GetRecord2 call # 0: After Wait For
GetRecord2 call # 0: exit
GetRecord2 call # 1: After Wait For
GetRecord2 call # 1: exit
and the following if Success = False in HandleJobAsync
Waiting for debugger to connect...
Program started.
GetRecord2 call # 0: entry
GetRecord2 call # 1: entry
GetRecord2 call # 0: Successful query. Retrieving data
GetRecord2 call # 0: Before Wait For
GetRecord2 call # 1: Successful query. Retrieving data
GetRecord2 call # 1: Before Wait For
Error reading response: (Exception) Not initialized
Error reading response: (Exception) Not initialized
Notice that nothing is executed after the Wait For in GetRecord2?

A solution may be to do a CallSubDelayed2 no matter what. In case of Success = False, return a Null value for the DBResult. This then would need checking in the calling code.
B4X:
Public Sub HandleJobAsync(Job As HttpJob, EventName As String)
   Dim ser As B4XSerializator
   Dim data() As Byte = Bit.InputStreamToBytes(Job.GetInputStream)
   ser.ConvertBytesToObjectAsync(data, "ser")
   Wait For (ser) ser_BytesToObject (Success As Boolean, NewObject As Object)
   Dim res As DBResult = Null
   If Success = False Then
       Log("Error reading response: " & LastException)
   Else
       res = NewObject
       res.Tag = Job.Tag
   End If
   CallSubDelayed2(mTarget, EventName & "_result", res)
   Return
End Sub
With this change, the logging (upon Success = False) produces
Waiting for debugger to connect...
Program started.
GetRecord2 call # 0: entry
GetRecord2 call # 1: entry
GetRecord2 call # 0: Successful query. Retrieving data
GetRecord2 call # 0: Before Wait For
GetRecord2 call # 1: Successful query. Retrieving data
GetRecord2 call # 1: Before Wait For
Error reading response: (Exception) Not initialized
Error reading response: (Exception) Not initialized
GetRecord2 call # 0: After Wait For
GetRecord2 call # 0: exit
GetRecord2 call # 1: After Wait For
GetRecord2 call # 1: exit
Note: This break existing code, so another option may be necessary
 

OliverA

Expert
Licensed User
Longtime User
This is not something that should normally happen
But if it does, there is no way to programmatically catch this as is. Yes, there is a log entry, but the programmer has no clue and there is a wait for that will never execute. I guess I can just have my own mod of DBRequestManager, but I was hoping for something more universal.
What causes ser_ConvertBytesToObjectsAsync to fail?
Maybe a failed hacking attempt? jRDC2 may not be well known, but it could become a target. Flipped bits that are not caught by networking checksums? Yes, I'm just guessing here. If at this point of the stage (after receiving an answer from jRDC2) if you think ser_ConvertBytesToObjectsAsync should not fail, why test for success at all in the original code? Why not just assign newObject to res and if something's gone wrong let the code bomb out? At least one could then use a try catch around HandleAsyncJobs if one is worried about failures. As the code stands right now, the only way to tell that a failure occurred is through a log entry.
 
Upvote 0

Erel

B4X founder
Staff member
Licensed User
Longtime User
Maybe a failed hacking attempt? jRDC2 may not be well known, but it could become a target. Flipped bits that are not caught by networking checksums? Yes, I'm just guessing here.
I don't think that this is really related.

If the data was downloaded properly (Job.Success = True) then the payload will be serialized properly. If it wasn't serialized properly then you probably made a programming mistake and you will see it during development.

why test for success at all in the original code?
Just because the API passed this flag. I agree that not testing for it is also fine.

There is no real issue here and I don't think that it is worth changing anything.
 
Upvote 0
Top