B4J Question Add Data from a jRDC2 Server to other jRDC2 Server.

Binary Soft

Member
Licensed User
Longtime User
Hello,
I want add data from a jRDC2 Server to other jRDC2 Server. So I add DBRequestManager module into jRDC2 project
When I query from 1st jRDC2 server to other jRDC2 Server. It's working.
B4X:
Dim req As DBRequestManager = CreateRequest
    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)
    Else
        Dim res As DBResult = Null
        Log($"Error executing query: ${j.ErrorMessage}"$)
    End If
    j.Release
    Return res

When I add data also. I use below code
B4X:
Dim req As DBRequestManager = CreateRequest
    Dim cmd As DBCommand
    cmd = CreateCommand("add_stock", Array(Null, 10, "Item 101"))
    Wait For (req.ExecuteBulk2(cmd, 0, Null)) JobDone(j As HttpJob)
    If j.Success Then
        req.HandleJobAsync(j, "req")
        Wait For (req) req_Result(res As DBResult)
        Log($"Success Size=${res.Rows.Size}"$)
    Else
        Log($"Error executing query: ${j.ErrorMessage}"$)
    End If
data are not added into 2nd server's database. But in 1st server show below logs

java.io.EOFException: Unexpected end of ZLIB input stream
at java.util.zip.InflaterInputStream.fill(InflaterInputStream.java:240)
at java.util.zip.InflaterInputStream.read(InflaterInputStream.java:158)
at java.util.zip.InflaterInputStream.read(InflaterInputStream.java:122)
at java.io.DataInputStream.readByte(DataInputStream.java:265)
at anywheresoftware.b4a.randomaccessfile.B4XSerializator.readByte(B4XSerializator.java:150)
at anywheresoftware.b4a.randomaccessfile.B4XSerializator.readObject(B4XSerializator.java:318)
at anywheresoftware.b4a.randomaccessfile.B4XSerializator.ReadObject(B4XSerializator.java:129)
at anywheresoftware.b4a.randomaccessfile.B4XSerializator.ConvertBytesToObject(B4XSerializator.java:99)
at anywheresoftware.b4a.randomaccessfile.B4XSerializator$2.call(B4XSerializator.java:110)
at anywheresoftware.b4a.randomaccessfile.B4XSerializator$2.call(B4XSerializator.java:1)
at anywheresoftware.b4a.BA$4.run(BA.java:292)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:750)

Error reading response: (EOFException) java.io.EOFException: Unexpected end of ZLIB input stream
How can I fix it?

Thanks.
 

OliverA

Expert
Licensed User
Longtime User
What is ExecuteBulk2?
 
Upvote 0

Binary Soft

Member
Licensed User
Longtime User
What is ExecuteBulk2?
Yes, ExecuteBulk2 is just a modify of ExecuteBatch. So I test with ExecuteBatch that exist in Server 1.
Data is add successful into 2nd jRDC2 server, but 1st server that run below code, show that text in log

B4X:
Dim req As DBRequestManager = CreateRequest
    Dim cmd As DBCommand
    cmd = CreateCommand("add_stock", Array(Null, 10, "Item 101"))
    Dim j As HttpJob = req.ExecuteBatch(Array(cmd), Null)
    Wait For(j) JobDone(j As HttpJob)
    If j.Success Then
        req.HandleJobAsync(j, "req")
        Wait For (req) req_Result(res As DBResult)
        Log($"Success Size=${res.Rows.Size}"$)
    Else
        Log($"Error executing query: ${j.ErrorMessage}"$)
    End If

Server 2 log:
Command: , took: 3ms, client=192.168.1.3
Success Size=1

Server 1 log:
java.io.EOFException: Unexpected end of ZLIB input stream
at java.util.zip.InflaterInputStream.fill(InflaterInputStream.java:240)
at java.util.zip.InflaterInputStream.read(InflaterInputStream.java:158)
at java.util.zip.InflaterInputStream.read(InflaterInputStream.java:122)
at java.io.DataInputStream.readByte(DataInputStream.java:265)
at anywheresoftware.b4a.randomaccessfile.B4XSerializator.readByte(B4XSerializator.java:150)
at anywheresoftware.b4a.randomaccessfile.B4XSerializator.readObject(B4XSerializator.java:318)
at anywheresoftware.b4a.randomaccessfile.B4XSerializator.ReadObject(B4XSerializator.java:129)
at anywheresoftware.b4a.randomaccessfile.B4XSerializator.ConvertBytesToObject(B4XSerializator.java:99)
at anywheresoftware.b4a.randomaccessfile.B4XSerializator$2.call(B4XSerializator.java:110)
at anywheresoftware.b4a.randomaccessfile.B4XSerializator$2.call(B4XSerializator.java:1)
at anywheresoftware.b4a.BA$4.run(BA.java:292)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:750)
Error reading response: (EOFException) java.io.EOFException: Unexpected end of ZLIB input stream
 
Last edited:
Upvote 0

OliverA

Expert
Licensed User
Longtime User
Server 2 log:
Command: , took: 3ms, client=192.168.1.3
This does not look right. ExecuteBatch2's return value is
B4X:
Return $"batch (size=${commands.Size})"$
In your case, with just sending one command, it should have been something like
Command: batch (size=1), took: 3ms, client=192.168.1.3

When I created my modded version of jRDC, I noticed some issues with error handling in the various methods and modded them to provide some (hopefully) better diagnostics in case something goes wrong. Here's my version of ExecuteBatch2:
B4X:
Private Sub ExecuteBatch2(con As SQL, in As InputStream, resp As ServletResponse) As String
    Dim ser As B4XSerializator
    Dim m As Map = ser.ConvertBytesToObject(Bit.InputStreamToBytes(in))
    Dim commands As List = m.Get("commands")
    Dim res As DBResult
    res.Initialize
    res.columns = CreateMap("AffectedRows (N/A)": 0)
    res.Rows.Initialize
    res.Tag = Null
    Dim errFlag As Boolean = False
    Dim errMsg As String = ""
    Dim sqlStmt As String
    Try
        con.BeginTransaction
        For Each cmd As DBCommand In commands
            'Sanity check cmd.Name
            sqlStmt = Main.rdcConnector1.GetCommand(cmd.Name)
            If sqlStmt <> "" Then
                ' Call correct NonQuery function based on cmd.Parameters setting
                If cmd.Parameters = Null Or cmd.Parameters.Length = 0 Then
                    con.ExecNonQuery(Main.rdcConnector1.GetCommand(cmd.Name))
                Else
                    con.ExecNonQuery2(Main.rdcConnector1.GetCommand(cmd.Name), cmd.Parameters)
                End If
            Else
                errFlag = True
                errMsg = $"jRDC ERROR: Command not found: ${cmd.Name}"$                
                Exit
            End If
        Next
        If Not(errFlag) Then
            res.Rows.Add(Array As Object(0))
            con.TransactionSuccessful
            Dim data() As Byte = ser.ConvertObjectToBytes(res)
            resp.OutputStream.WriteBytes(data, 0, data.Length)
        Else
            con.Rollback
            Log(errMsg)
            resp.SendError(500, errMsg)
        End If
    Catch
        con.Rollback
        Log(LastException)
        resp.SendError(500, LastException.Message)
    End Try
    Return $"batch (size=${commands.Size})"$
End Sub
It should be a drop-in replacement, but it offers better diagnostics which hopefully can help you pinpoint where the issue is/what the issue is.

Link: https://www.b4x.com/android/forum/threads/modded-jrdc2-w-sqlite-support-and-more.85578/
(yeah, shameless plug...)

Sigh, even my return value is very unhelpful in case of failure...
 
Upvote 0

Binary Soft

Member
Licensed User
Longtime User
This does not look right. ExecuteBatch2's return value is
B4X:
Return $"batch (size=${commands.Size})"$
In your case, with just sending one command, it should have been something like


When I created my modded version of jRDC, I noticed some issues with error handling in the various methods and modded them to provide some (hopefully) better diagnostics in case something goes wrong. Here's my version of ExecuteBatch2:
B4X:
Private Sub ExecuteBatch2(con As SQL, in As InputStream, resp As ServletResponse) As String
    Dim ser As B4XSerializator
    Dim m As Map = ser.ConvertBytesToObject(Bit.InputStreamToBytes(in))
    Dim commands As List = m.Get("commands")
    Dim res As DBResult
    res.Initialize
    res.columns = CreateMap("AffectedRows (N/A)": 0)
    res.Rows.Initialize
    res.Tag = Null
    Dim errFlag As Boolean = False
    Dim errMsg As String = ""
    Dim sqlStmt As String
    Try
        con.BeginTransaction
        For Each cmd As DBCommand In commands
            'Sanity check cmd.Name
            sqlStmt = Main.rdcConnector1.GetCommand(cmd.Name)
            If sqlStmt <> "" Then
                ' Call correct NonQuery function based on cmd.Parameters setting
                If cmd.Parameters = Null Or cmd.Parameters.Length = 0 Then
                    con.ExecNonQuery(Main.rdcConnector1.GetCommand(cmd.Name))
                Else
                    con.ExecNonQuery2(Main.rdcConnector1.GetCommand(cmd.Name), cmd.Parameters)
                End If
            Else
                errFlag = True
                errMsg = $"jRDC ERROR: Command not found: ${cmd.Name}"$               
                Exit
            End If
        Next
        If Not(errFlag) Then
            res.Rows.Add(Array As Object(0))
            con.TransactionSuccessful
            Dim data() As Byte = ser.ConvertObjectToBytes(res)
            resp.OutputStream.WriteBytes(data, 0, data.Length)
        Else
            con.Rollback
            Log(errMsg)
            resp.SendError(500, errMsg)
        End If
    Catch
        con.Rollback
        Log(LastException)
        resp.SendError(500, LastException.Message)
    End Try
    Return $"batch (size=${commands.Size})"$
End Sub
It should be a drop-in replacement, but it offers better diagnostics which hopefully can help you pinpoint where the issue is/what the issue is.

Link: https://www.b4x.com/android/forum/threads/modded-jrdc2-w-sqlite-support-and-more.85578/
(yeah, shameless plug...)

Sigh, even my return value is very unhelpful in case of failure...
Thanks, I got it.
 
Upvote 0

OliverA

Expert
Licensed User
Longtime User
Upvote 0

Binary Soft

Member
Licensed User
Longtime User
jRDC2 has separate Query and Batch functions. I add new function AddAndQuery that insert data and then I check my stock's quantity and return reply to client.
In my AddAndQuery function, my issue is during adding transactions I query them. So I add Wait For into add code and then query.
 
Upvote 0

OliverA

Expert
Licensed User
Longtime User
So I add Wait For into add code and then query.
So is your issue resolved? Are you using wait for in the server handler? If so, you have to use a message loop in order for the wait for to work as expected
 
Upvote 0

Binary Soft

Member
Licensed User
Longtime User
Thank OliverA.


B4X:
Public Sub addSale(strVNoHeader As String, bulkData As String) As ResumableSub
    Try
        Dim lst As List
        lst.Initialize
        Dim req As DBRequestManager = CreateRequest
        Dim cmd As DBCommand
        cmd = CreateCommand("add_sale", Array(strVNoHeader, bulkData))
        Wait For (req.ExecuteLink2(cmd, 0, Null)) JobDone(j As HttpJob)    'Check 1
        If j.Success Then                                                   'Check 2
            req.HandleJobAsync(j, "req")
            Wait For (req) req_Result(res As DBResult)
            Dim strCode, strUnit As String
            For Each row() As Object In res.Rows
                strCode = row(res.Columns.Get("Code"))
                strUnit = row(res.Columns.Get("Unit"))
                lst.Add(strCode & "," & strUnit)
            Next
        Else
            Log($"Error executing add and query: ${j.ErrorMessage}"$)
        End If
        j.Release
        Return lst
    Catch
        Log("sale add err")
        Return lst
    End Try
End Sub


Call in RDCHandler
'add into stock item code and quantity into this 1st server' db

'if stock is out of stock try to add 2nd server as below
B4X:
Wait For (addSale(strVNoHeader, strDU)) Complete(lst As List)
If lst.Size > 0 Then
    lstMessage.Clear
    lstMessage.AddAll(lst)
End If

after Check 1(in addSale) line is run. j.Success is true. required data is inserted into 2nd server's db and get which items and unit is added in my lst.

But problem is return send data to client android app
So I debug from check 1 line. below function that exist in DBRequestManager return data to 1st server and also to client android. But data is 0 in client android.
when received data is 0 in DBRequestManager's HandleJobAsync. program got error. So I add data.Length > 0.
B4X:
Private Sub SendJob(j As HttpJob, Data() As Byte, Tag As Object, Method As String) As HttpJob
    j.Tag = Tag
    j.PostBytes(link & "?method=" & Method , Data)
    Return j
End Sub
Then resp that exist after Wait For addSale is to reply to client android.
When I debug and check res variable. All result is correct. But It result data is not send to client.

B4X:
Dim res As DBResult
        res.Initialize
        res.columns.Initialize
        res.Tag = Null 'without this the Tag property will not be serializable.
        res.columns.Put("Code", 0)
        res.columns.Put("Unit", 1)
        res.Rows.Initialize
        Log(lstMessage.Size & " =lstMessage.Size")
        For Each item As String In lstMessage
            Dim parts() As String = Regex.Split(",", item)
            Dim row(2) As Object
            row(0) = parts(0)
            row(1) = parts(1)
            Log($" ${row(0)} ${row(1)}"$)
            res.Rows.Add(row)
        Next
        Dim data() As Byte = ser.ConvertObjectToBytes(res)
        resp.OutputStream.WriteBytes(data, 0, data.Length)   'this line is runn but not reach to client
        Return "query: " & cmd.Name

1. I client's issue is solved by add data.Length > 0.
2. I add data from 1st server to 2nd server.
3. I reply added data from 2nd server at 1st server.
But I can not solve this issue the reply data from 2nd server to client.
 
Last edited:
Upvote 0

OliverA

Expert
Licensed User
Longtime User
Actually
resp.OutputStream.WriteBytes(data, 0, data.Length) 'this line is runn but not reach to client
this is never reached, since anything after
Wait For (addSale(strVNoHeader, strDU)) Complete(lst As List)
will not run if you place a Wait For in a server handler without using a message loop.

If you post the RDCHanlder you are using (with dummy values for any sensitive variables), I'll fix the code for Wait For and work as expected. You can also PM me your code and I'll fix it up, with the full solution in the PM and partials posted here for others to follow in case they encounter the same issue.

Note: "will not run if you place a Wait For in a server handler without using a message loop" - this may work differently under the debugger, but in release mode, anything after the Wait For in that sub will not run in a server handler without the explicit use of a message loop
 
Upvote 0

Binary Soft

Member
Licensed User
Longtime User
Actually

this is never reached, since anything after

will not run if you place a Wait For in a server handler without using a message loop.

If you post the RDCHanlder you are using (with dummy values for any sensitive variables), I'll fix the code for Wait For and work as expected. You can also PM me your code and I'll fix it up, with the full solution in the PM and partials posted here for others to follow in case they encounter the same issue.

Note: "will not run if you place a Wait For in a server handler without using a message loop" - this may work differently under the debugger, but in release mode, anything after the Wait For in that sub will not run in a server handler without the explicit use of a message loop
Thank OliverA,

I sent my project zip file link in PM.
 
Upvote 0

OliverA

Expert
Licensed User
Longtime User
B4X:
'Handler class
Sub Class_Globals
    '....
    'Some global variables here
    '....
    'Below required to capture the return value of the AddAndQuery method, since with usage of
    'StartMessageLopp/StopMessageLoop there will be no Return <somevalue>
    'as implemented here
    Private qq As String
End Sub

B4X:
Sub Handle(req As ServletRequest, resp As ServletResponse)
'...
'... Some code here
'...
        Else if method = "link2" Then
            Log("Linked2")
            'Wait For (AddAndQuery(con, in, resp)) Complete(qq As String)
            'Note: The Wait For is replaced with
            '    a) A private global qq that will be used to "return" the results of AddAndQuery
            '        This is used since we cannot depend on AddAndQuery to return a result due to the
            '        Wait For (or - not used here, but for complete record - Sleep) usage within the
            '        method
            '    b) A plain call to the method (in this case AddAndQuery) without the usage of Wait For
            '    c) StartMessageLoop is used to start processing events after AddAndQuery returns from its
            '        Wait For or Sleep statements
            '    d) assignment of the global variable to the desired variable
            AddAndQuery(con, in, resp)
            StartMessageLoop
            'Execution will return to here AFTER StopMessageLoop is called
            q = qq
'...
'... More code here
'...

B4X:
Private Sub AddAndQuery(con As SQL, in As InputStream,  resp As ServletResponse) As ResumableSub
    'Note: Every use of Wait For and/or Sleep will cause an immediate return out of the sub to the
    'calling sub. That is why StartMessageLoop has to be used in the calling sub in order for
    'Wait For and/or Sleep events to process
'....
'... Code
'....
     Try
'
' Code
'
        Dim data() As Byte = ser.ConvertObjectToBytes(res)
        resp.OutputStream.WriteBytes(data, 0, data.Length)
        'Return "query: " & cmd.Name
        'We will use StopMessageQueue - See Below
    Catch
        'con.Rollback
        Log(LastException)
        resp.SendError(500, LastException.Message)
        'Return "query: " & cmd.Name
        'We will use StopMessageQueue - See Below
    End Try
    'Using SmartStrings - just for fun, and because it's more
    'efficient when it comes to string building
    qq = $"query: ${cmd.Name}"$
    'StopMessageLoop
    'Processing will resume to AFTER the StartMessageLoop used in Sub Handle above
    StopMessageLoop
    Return True ' Will never be reached
End Sub

Note 1: Untested, but it should work
Note 2: For more explanation on Wait For/Sleep behavior see https://www.b4x.com/android/forum/threads/b4x-resumable-subs-sleep-wait-for.78601/
 
Upvote 0

Binary Soft

Member
Licensed User
Longtime User
B4X:
'Handler class
Sub Class_Globals
    '....
    'Some global variables here
    '....
    'Below required to capture the return value of the AddAndQuery method, since with usage of
    'StartMessageLopp/StopMessageLoop there will be no Return <somevalue>
    'as implemented here
    Private qq As String
End Sub

B4X:
Sub Handle(req As ServletRequest, resp As ServletResponse)
'...
'... Some code here
'...
        Else if method = "link2" Then
            Log("Linked2")
            'Wait For (AddAndQuery(con, in, resp)) Complete(qq As String)
            'Note: The Wait For is replaced with
            '    a) A private global qq that will be used to "return" the results of AddAndQuery
            '        This is used since we cannot depend on AddAndQuery to return a result due to the
            '        Wait For (or - not used here, but for complete record - Sleep) usage within the
            '        method
            '    b) A plain call to the method (in this case AddAndQuery) without the usage of Wait For
            '    c) StartMessageLoop is used to start processing events after AddAndQuery returns from its
            '        Wait For or Sleep statements
            '    d) assignment of the global variable to the desired variable
            AddAndQuery(con, in, resp)
            StartMessageLoop
            'Execution will return to here AFTER StopMessageLoop is called
            q = qq
'...
'... More code here
'...

B4X:
Private Sub AddAndQuery(con As SQL, in As InputStream,  resp As ServletResponse) As ResumableSub
    'Note: Every use of Wait For and/or Sleep will cause an immediate return out of the sub to the
    'calling sub. That is why StartMessageLoop has to be used in the calling sub in order for
    'Wait For and/or Sleep events to process
'....
'... Code
'....
     Try
'
' Code
'
        Dim data() As Byte = ser.ConvertObjectToBytes(res)
        resp.OutputStream.WriteBytes(data, 0, data.Length)
        'Return "query: " & cmd.Name
        'We will use StopMessageQueue - See Below
    Catch
        'con.Rollback
        Log(LastException)
        resp.SendError(500, LastException.Message)
        'Return "query: " & cmd.Name
        'We will use StopMessageQueue - See Below
    End Try
    'Using SmartStrings - just for fun, and because it's more
    'efficient when it comes to string building
    qq = $"query: ${cmd.Name}"$
    'StopMessageLoop
    'Processing will resume to AFTER the StartMessageLoop used in Sub Handle above
    StopMessageLoop
    Return True ' Will never be reached
End Sub

Note 1: Untested, but it should work
Note 2: For more explanation on Wait For/Sleep behavior see https://www.b4x.com/android/forum/threads/b4x-resumable-subs-sleep-wait-for.78601/
I tried to fix it, and it took four days.
Now, it's working now.
Thank you for your help and attention.
 
Upvote 0
Top