B4J Library Modded jRDC2 w/SQLite support and more

OliverA

Expert
Licensed User
Attached you'll find a modified version of jRDC2 (source https://www.b4x.com/android/forum/t...ation-of-rdc-remote-database-connector.61801/) that supports SQLite as the datastore out of the box. Support for V1 has been removed and therefore this is not a drop in replacement for jRDC2. This is more meant for people starting new with jRDC2. Comments are welcome (pro and con). Other modifications are listed in the main file and are relisted here:

'******
'2017/10/30 Update
'******
'Main module changes:
'-------------------
'Added the logging of one of the IP addresses that the server is bound to. The method.
' to retrieve the IP address is similar to ServerSocket's GetMyIP method. It was adapted
' from https://issues.apache.org/jira/browse/JCS-40.
'Added ability to assign server to a specific IP address.

'RDCConnector changes:
'--------------------
'Added IPAddress configuration option. Allows to set the jRDC server's IP address. An un-bindable
' address will crash the server.
'Added HasIPAddress and GetIPAddress to access IPAddress configuration.
'Added support for SQLite. If the DriverClass contains SQLite (case insensitive), then jRDC
' is configured for SQLite usage. SQLite does not use pools. Pool/non-pool handling gleaned
' from DBM.bas module of ABMaterial.
'Added CreateFile configuration option. This option is used for the SQLite backend
' (if used). If set to True or set to 1, then the SQLite database will be created in
' case it does not exist yet. Any other settings are interpreted as False.
'Added PoolSize configuration option. This is the size of the pool that should be used
' for pooled JDBC databases. This option was gleaned from the DBM.bas module of ABMaterial.
'Modified GetCommand to return empty string ("") when no command found.
'Modified GetConnection to handle non-pool SQL connections (SQLite).

'RDCHandler changes:
'-------------------
'Modified Handler to not close the databas connection in case a pool is not used, else SQLite
' will not function properly
'Modified ExecuteQuery2 and ExecuteBatch2 to check cmd.Parameters for Null. This removed a
' Null pointer exception error message in case cmd.Parameters was Null. Also call
' ExecQuery/ExecNonQuery when no cmd.Parameters are given.
'Modified ExecuteQuery2 and ExecuteBatch2 to check for valid command. If no valid command found,
' return 500 error with proper response. This takes care of a syntax exception error for the DB.
' in case of MySQL it looks something like this:
' com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax;
' check the manual that corresponds to your MySQL server version for the right syntax to use near 'null' at line 1
'Modified Try/Catch block in ExecuteBatch2. Moved the code below the End Try that dealt with
' converting the DBResult object and sending it to the client into end of the Try block. This
' took care of an java.lang.IllegalStateException: WRITER exception caused by OutputStream when
' called after the Catch's SendError.
'Added Try/Catch block to ExecuteQuery2. This should prevent the following on the B4X client
' side: java.io.EOFException: Unexpected end of ZLIB input stream
'Removed all jRDC v1 functionality.

'Miscellaneous notes:
'-------------------
'The config.properties contains sql. directives that pertain to a jRDC version of the DBUtils
' demo.
'Per usual, please ensure you have the proper #AdditionalJar set for the database backend
' of your choice (even for SQLite).
 

Attachments

inakigarm

Well-Known Member
Licensed User
Thanks Oliver; I'm working this days on a project that uses jRDC2 in a client -server scheme (Sqlite on local and Sqlite on server, synchronising the data on server with clients data)
One mod I've done on jRDC2 is retrieving the sql commands from a sqlite db also (configProperties.db) because:
- It's easier to modify/add sql commands from an helper application than the config.Properties file ( and as I'm not a very good programmer always I'very to modify the projected application in one way or another
- Gets the project 'tidy' and more understandable
- It'easier to apply the new added commands without the need of resetting the JRDC server application

If you want, I can send you the code (obviously, it's not a complex mod)
 

OliverA

Expert
Licensed User
Specifically for SQLite to work correctly in a concurrent environment you must:
- Set the journal mode to wal.
- Use a single SQL connection initialized with InitializeSQLite.
Both have been accounted for.
 

OliverA

Expert
Licensed User
and as I'm not a very good programmer always I'very to modify the projected application in one way or another
Same here. Both my "contributions" are based on already posted projects (Universal DBUtils and now this).
One mod I've done on jRDC2 is retrieving the sql commands from a sqlite db
Ha, I was going to take a stab at this next (a means of updating the SQL statements without restarting server).
If you want, I can send you the code
That would be great!
 

Diceman

Active Member
Licensed User
Oliver, I may have discovered a bug, or maybe I'm missing something. Can you shed some light on it?

1) In HandleJobAsync the CallSubDelayed2() gets executed but the sub "req_result" is never executed. I have a breakpoint inside of "req_result" and a log statement and they never execute. I thought maybe it can't find the sub so I added an "if statement" to make sure the sub "result_req" exists before the CallSubDelated2() statement is executed.

2) I had to put the "req_result" sub in jRDC2Utils because that is where mTarget is pointing to. The debugger shows mTarget.Name = "b4j.example.jrdc2utils". I would have thought req_result would belong in Main. I have one in there too, and it can't find it there either.

Do you have any idea why req_result() sub is never executed? BTW, req_result() sub is also called in jRDC2Utils.ExecuteQuery but the code in req_result() never executes there either. I tried both Debug and Release modes.

TIA

B4X:
'This is the code from DBRequestManager

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
    If SubExists(mTarget, EventName & "_result") Then
        Log("Calling " & EventName & "_result")     '<-- This line gets executed and is shown in log  
        CallSubDelayed2(mTarget, EventName & "_result", res)    '<-- Line gets executed but sub is never executed (log stmt or breakpoint in "req_results" never executed)
    Else
        Log("Sub Does Not Exist: " & EventName & "_result")
    End If
End Sub
B4X:
'This is the sub in jRDC2Utils that never executes.

public Sub req_result(aDbResult As DBResult)
    Log("[jRDC2Utils.req_result] EXECUTED")
End Sub
 

OliverA

Expert
Licensed User
I'm going to need more code from you to see what you are doing code wise (how you use DBRequestManager and/or JRDC2Utils) before I can give you an answer. As is, JRDC2Utils wraps DBRequestManager and handles the interception of the _result callback via Wait For(s).
 

Diceman

Active Member
Licensed User
I'm going to need more code from you to see what you are doing code wise (how you use DBRequestManager and/or JRDC2Utils) before I can give you an answer. As is, JRDC2Utils wraps DBRequestManager and handles the interception of the _result callback via Wait For(s).
So where should the user's actual "req_result" sub be defined? I haven't been able to get any req_result() sub to be executed whether it is defined in Main or jRDC2Utils. Is it operator error (me?) or a problem with the code? I define events all the time for the classes I define, but I never had to use "Wait For..." when calling an event.

I have added tags "##:" throughout the client that explains the problem.
There is No rush at looking at it. It is NOT holding me up. Whenever you get around to it. Thanks.
 

Attachments

OliverA

Expert
Licensed User
So where should the user's actual "req_result" sub be defined?
When you use the jRDC2Utils module, you do not have to define, nor worry about "req_result". It is taken care of by the ExecuteQuery method of the jRDC2Utils module and this method returns the DBResult object that is returned by DBRequestManager's HandleJobAsync method. If you want to take care of the results yourself that are returned by HandeJobAsync, then
1) Use ExecuteQuery2 of the jRDC2Utils module, which allows you to provide a caller object and method name to execute after retrieving the results (technically it's just a wrapper of ExecuteQuery)
or
2) Don't use jRDC2Utils, but just follow the examples given by @Erel's post on jRDC2 client usage (https://www.b4x.com/android/forum/t...ation-of-rdc-remote-database-connector.61801/).

Answer to comments on the code you supplied:
You're going to hate me for mentioning this but, :)
How is this going to work if calling rtn Main, is accessing more than one server? (rdcLink will have the value of the last Initialize rdcUrl parameter).
It is common to use more than one server. A server that interacts with the user (client) and this server would get the data from another server. Or backup the data on the server to another server.
One solution (preferred) would be to make jRDC2Utils a class (jRDC2Mgr?). This would solve the Event problem and allow you to add properties if needed at a later date.
The 2nd solution would be to pass the rdcLink as a parameter in each of your SQL calls as well as the event name (see "##:?" below).
Nope, no hate from me. Yes, as it stands, jRDC2Utils works with one server and you provided two means it could be changed to handle more. My only comment here is that no event name is necessary, since jRDC2Utils either 1) takes care of that or 2) allows you to specify caller/method in the ExecuteQuery2 method.
' Dim selectedRowValue As Object = TableView1.SelectedRowValues(0) '##:BUG
'##:Fix 2018-07-14 If no row is selected, selectedRowValue is NULL
Dim selectedRowValue As Object=Null 'Fix
If selectedRow >= 0 Then 'Fix
selectedRowValue = TableView1.SelectedRowValues(0) 'Fix
End If 'Fix
Strange. At what point is ValueChanged method called without having a row selected? Please note since my code is pretty much a straight rip of the DBUtils example, that bug would also be manifest there.

Note: Technically all these discussions should be happening at the jRDC2 client example that uses this code (https://www.b4x.com/android/forum/threads/jrdc2-client-example-using-modded-jrdc2.85581/).
 

Diceman

Active Member
Licensed User
OliverA, thanks for your reply. I will try your suggestions and get back to you with the results.
 

Diceman

Active Member
Licensed User
When you use the jRDC2Utils module, you do not have to define, nor worry about "req_result". It is taken care of by the ExecuteQuery method of the jRDC2Utils module and this method returns the DBResult object that is returned by DBRequestManager's HandleJobAsync method. If you want to take care of the results yourself that are returned by HandeJobAsync, then
1) Use ExecuteQuery2 of the jRDC2Utils module, which allows you to provide a caller object and method name to execute after retrieving the results (technically it's just a wrapper of ExecuteQuery)
or
2) Don't use jRDC2Utils, but just follow the examples given by @Erel's post on jRDC2 client usage (https://www.b4x.com/android/forum/t...ation-of-rdc-remote-database-connector.61801/).
Ok, I don't know how I missed seeing the sub ExecuteQuery2 that has an event parameter. Thanks.

Answer to comments on the code you supplied:

Nope, no hate from me. Yes, as it stands, jRDC2Utils works with one server and you provided two means it could be changed to handle more. My only comment here is that no event name is necessary, since jRDC2Utils either 1) takes care of that or 2) allows you to specify caller/method in the ExecuteQuery2 method.
I will do some rudimentary benchmarking to see if changing jRDCUtils2 into a class causes any speed or memory problems if used on the server. I'm thinking of having one server call one or more other servers to get more data. It may be simpler just to pass the parameters to the jRDC2 code module so garbage collection is easier.

Strange. At what point is ValueChanged method called without having a row selected? Please note since my code is pretty much a straight rip of the DBUtils example, that bug would also be manifest there.
I have included a couple of screenshots that may help. It is a trivial fix.

Note: Technically all these discussions should be happening at the jRDC2 client example that uses this code (https://www.b4x.com/android/forum/threads/jrdc2-client-example-using-modded-jrdc2.85581/).
Correct. I redownloaded these 2 files this morning and retested.

Thanks again for your help. I'm up and running again. :)

ClientInstructions.png ClientException.png
 
Top