B4J Question B4J Web Server RDC vs PHP...

Cableguy

Expert
Licensed User
Hi guys...

I've just set up my vps, and thus decided its time to dig into that misterious world of RDC and databases...

So, from what I could gather, I have two choices...

  1. RDC
  2. PHP
The first implies running an extra server that will listen to incoming requests, the second is somewhat more limited in its capabilities... (please correct me if needed...)

So for a completely novice in this kind of stuff, I need you to help me help myself...
 

Harris

Expert
Licensed User
What VPS do you have - Win or Linux? What database are you using? (MySQL hopefully as I do).

I can send you some files to explore of my setup with RDC. It isn't all that difficult when you know the basics.
Our good friend, @warwound help me set mine up and it works great!

Remember, RDC is for data in your DB. Images, PDF docs and other files must use a different method to up/download to and from devices.
 

KMatle

Expert
Licensed User
Definitely: PHP

Why?

RDC is just to call a database function, PHP is a programming language which also can use databases

1. Almost all providers offers it with MySQL for a few $$/€€ (my provider: unlimited db's 1 GB each for 10€/month)
2. You can add logic to the php scripts and handle everything (like I use GCM from my scripts)
3. It can be called from ALL platforms/devices (low level api)
 

Cableguy

Expert
Licensed User
I was just browsing your "for beginers: " series...
you say, and are Right of course, to never directly enter the database credential in the script itself, so I recon you mean we should pass them along when calling the script, Right? How?
 

Cableguy

Expert
Licensed User
What VPS do you have - Win or Linux? What database are you using? (MySQL hopefully as I do).

I can send you some files to explore of my setup with RDC. It isn't all that difficult when you know the basics.
Our good friend, @warwound help me set mine up and it works great!

Remember, RDC is for data in your DB. Images, PDF docs and other files must use a different method to up/download to and from devices.
I am using "1 and 1" vps service, a basic one for starters...
 

KMatle

Expert
Licensed User
Right? How?
Just do an include from another file. The reason is: For the unlikely case the apache server doesn not serve correct, maybe the script is shown (which is bad). So you have one file which only has two includes: The credentials and the code. So it is safe.

Additionally check all the values, the ip address from the caller, use a userid & pw (or a API-KEY like Google does) check login tries, do encryption and many more. Browse for "SQL Injection" and how to protect (escaping all values).

The reason why many providers prevent using databases directly (from the outside) is for security reasons. Only the php script can access the db ("local") on most providers. More security.
 

Cableguy

Expert
Licensed User
Just do an include from another file. The reason is: For the unlikely case the apache server doesn not serve correct, maybe the script is shown (which is bad). So you have one file which only has two includes: The credentials and the code. So it is safe.

Additionally check all the values, the ip address from the caller, use a userid & pw (or a API-KEY like Google does) check login tries, do encryption and many more. Browse for "SQL Injection" and how to protect (escaping all values).

The reason why many providers prevent using databases directly (from the outside) is for security reasons. Only the php script can access the db ("local") on most providers. More security.
that's way too advanced for me at the moment
can you elaborate a bit more on the includes, maybe a small example?
 

Harris

Expert
Licensed User
That's where RDC is safe. No Injection...
You just send the script name and the data...

B4X:
#Lines starting with '#' are comments.

#Backslash character at the end of line means that the command continues in the next line.

#MySQL Server
DriverClass=com.mysql.jdbc.Driver
JdbcUrl=jdbc:mysql://localhost/harris?characterEncoding=utf8

#JdbcUrl=jdbc:mysql://localhost/home/harris/b4a-rdc/jdbc_driver?characterEncoding=utf8
#SQL Server
#DriverClass=net.sourceforge.jtds.jdbc.Driver
#JdbcUrl=jdbc:jtds:sqlserver://<database server ip>/<database>

# MySQL DB username and password
User=harris
Password=xyZ1234

# the port used
ServerPort=17178

#If Debug is true then this file will be reloaded on every query.
#This is useful if you need to modify the queries.
Debug=true

#commands

# these ones inserts a record.  You need to specify which fields receive the param values in the correct order.
sql.load_mast=INSERT INTO loadmast (mastid,loadtype,acttype,recdate,drvid,trkid,trl1id,trl2id,trl3id,load_dur,wait_dur,rectype,lat,lon,place,compid,sent,odom,fuel,sourceid,swver,comment) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)

sql.load_det=INSERT INTO loaddet (mastid,ival1,ival2,ival3,sval1,sval2,sval3,dval1,dval2,compid) VALUES (?,?,?,?,?,?,?,?,?,?)

sql.event_mast=INSERT INTO eventmast (mastpk,sdate,edate,evtviol,evtzone,evttype,drvid,trknum,sodo,eodo,ival1,ival2,sval1,sval2,dval1,dval2,compid,sent,lat,lon,location,spare,sourceid,swver,comment) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)


# this one gets a count of records to determine if anything has been updated since last check.  
sql.get_zone_upd=SELECT COUNT(*) AS num FROM zonemast where UNIX_TIMESTAMP(updated) > ? and comp_id = ?

# if the above count was > 0, then let us update the device with new configuration data...
# since these tables are small (100 records or so), I empty the table on the device and get everything from the server.
# I find this much easier than trying to determine what was edited, what is new and what was deleted. 
# also, all configuration stuff should be handled on the server and distributed to devices. Devices collect data based on configs and pass it back to server. 
sql.get_zonemast=SELECT pk, mastpk, name, speed, stype,s1,s2,s3,s4,s5,s6,d1,d2,t1,t2,comment,active  FROM zonemast where comp_id = ?
sql.get_zonedet=SELECT pk, mastpk, lat, lon FROM zonedet where comp_id = ?
More to follow, but I have to dig for it.

Read the RDC guides and this will begin to make sense.
 

KMatle

Expert
Licensed User
Attached an example:

Main: This will be called by OkHttpUtils and includes the following two

Cred: Credentials
Code: The script ittself

Usually you use more complex names for the last two.
 

Attachments

Harris

Expert
Licensed User
@Harris : No battle here :)

I have a question: How do you secure the call of the script? Scriptname & Data. No Userid & Pw?
Of course not... we are just trying to help out our fellow members with alternatives and suggestions...

I am mining my B4A app code and will post some examples shortly.
I have been buried in ABMaterial the past many months - and all of this is mute.
That said however, my B4A app serves the data to MySQL; ABM app allows users to view, report and create the configurations that the app relies upon.
 

Enrique Gonzalez R

Well-Known Member
Licensed User
I also bought a small server for 1 and 1.

Installed nginx (instead of apache) mariadb (instead of mysql) and php 7.0

But I only installed php to have WordPress. I am not planning to use it for the communication with my apps

At the end php and RDC works as the same way; Avoiding a direct connection with the database from outside of your server.
 

Harris

Expert
Licensed User
Some B4A code:

B4X:
Sub GetZonesupd

' This method checks the ZoneMast table to see if anything is new...
' The "GetLastUPDCheck" (get last update check) returns the date when it was last updated - locally on the device
' This date is checked against the table timestamp field (updated), to see if any server records are newer 
' It will return the count - 0 or greater - if greater then 0, do the next step...

     Dim cmd As DBCommand
    cmd.Initialize
    cmd.Name = "get_zone_upd"
    Dim mdt As Long
    mdt = LogCM.GetLastUPDCheck(cmd.Name)
    cmd.Parameters = Array As Object(mdt, DefCM.Company_id)
    Log(" op request: "&mdt&"  "&DateTime.date(DateUtils.UnixTimeToTicks(mdt))&"  "&DateTime.time(DateUtils.UnixTimeToTicks(mdt))&"  "&DefCM.Company_id)
    LogCM.reqManager.ExecuteQuery( cmd, 0, "get_zone_upd")
  '  ToastMessageShow("Requesting Zone Update Count",False)

End Sub

B4X:
Sub JobDone(Job As HttpJob)

' these are the results from the server ...
' my var naming convention is something to behold - cause I copy and paste alot...
' anyway, if result is greater than 0 - do the heavy stuff... GetAllZones

           If result.Tag = "get_zone_upd" Then 'query tag
                For Each records() As Object In result.Rows
                   Dim name As String = records(0)
                   Log("Zone record count: "&name)
                Next
                If name > 0 Then
                      ToastMessageShow("Some GeoZones to Update",True)
                   Log("Some Zones to update "&name)
                   LogCM.GetAllZones
                'LogCM.SetLastUPDCheck(result.Tag)
                CallSubDelayed2(LogServmod,"SetLastUPDCheck",result.Tag)

                Else
                  ' ToastMessageShow("No GeoZones to Update",True)
                   Log("No Zones to update ")
               
                End If
            End If
B4X:
Sub GetAllZones

' this posts two more jobs...
' since the server can contain data and configs for more than 1 company, I send which company data we are looking for...

    'ProgressDialogShow("Fetching results from our servers...")
  '  DoEvents
     Dim cmd As DBCommand
    cmd.Initialize
    cmd.Name = "get_zonemast"
    cmd.Parameters = Array As Object(DefCM.Company_id)
    reqManager.ExecuteQuery( cmd, 0, "get_zonemast")
 

    Dim cmd As DBCommand
    cmd.Initialize
    cmd.Name = "get_zonedet"
    cmd.Parameters = Array As Object(DefCM.Company_id)
    reqManager.ExecuteQuery( cmd, 0, "get_zonedet")
 
End Sub

'  the " get_zonemast " job posting...
' create a map with data from result (GetRecsTable - method shown at bottom)


        If result.Tag = "get_zonemast" Then 'query tag
                Dim EmpMap As Map
                EmpMap.Initialize
               EmpMap = LogCM.reqManager.GetRecsTable(result)
                'Log("Device map: "&EmpMap)
                UpdateZoneMast(EmpMap)
                zoneCM.zoneinit = False
               
         End If       


' drop the existing table and build a new one... IF the map size is greater than 0 (safety first!)

Sub UpdateZoneMast(Mainmap As Map)
If Mainmap.Size = 0 Then
   Return
End If
'sql.get_zonemast=Select pk, mastpk, name, speed, stype,comment  FROM zonemast 
'sql.get_zonedet=Select pk, mastpk, lat, lon FROM zonedet 

DefCM.SQL1.ExecNonQuery("DROP TABLE IF EXISTS zonemast") 
DefCM.SQL1.ExecNonQuery("CREATE TABLE zonemast (pk INTEGER, mastpk INTEGER,  name TEXT, speed INTEGER, stype INTEGER,  s1 INTEGER, s2 INTEGER, s3 INTEGER, s4 INTEGER, s5 INTEGER,s6 INTEGER, d1 REAL, d2 REAL, t1 TEXT, t2 TEXT, comment TEXT, active INTEGER )")
Dim i, j As Int 

    For i = 0 To Mainmap.Size - 1
        Dim sb As StringBuilder
        sb.Initialize
        Dim M As Map
        M.Initialize
        M = Mainmap.GetValueAt(i)
        sb.Append("INSERT INTO zonemast VALUES( ")
        For j = 0 To M.Size - 1
         sb.Append("'"&M.GetValueAt(j)&"'")
         If (j < M.Size - 1) Then
          sb.Append( ", ")
         End If
        Next
        sb.Append( ")")
'        Log(" sql: "&sb.ToString)
        DefCM.SQL1.ExecNonQuery(sb.ToString)
    Next
    zoneCM.zoneinit = False
End Sub 


' the funcky sub for building a map with data results...
' I modified Erel's example to fit my needs

Public Sub GetRecsTable(Table As DBResult) As Map
    Dim Mainmap As Map
    Dim Submap, Colmap As Map
    Mainmap.Initialize
    Submap.Initialize
    Colmap.Initialize
   
    Dim i,j As Int
    i = 0
    j = 0
   
    For Each col In Table.Columns.Keys
           Colmap.Put(i, col)
          i = i + 1       
    Next
    'Log(" Column map: "&Colmap)
   
    For Each row() As Object In Table.Rows
        i = 0
        j = j + 1
        Submap.Initialize
        For Each record As Object In row
            Dim r As String
            r = record
            r = r.Replace("'"," ")
            Submap.Put(Colmap.Get(i),r)
           i = i + 1
        Next
        Mainmap.Put(j, Submap)
    Next
    'Log("Mainmap 2")
   
    For i = 0 To Mainmap.Size -1
        Dim m As Map
        m.Initialize
        m = Mainmap.GetValueAt(i)
    Next
    Return Mainmap
   
End Sub
Note that we start this process by seeing if anything new exists on the server.
Then we call other jobs that call other methods - if everything succeeded.

It took me awhile to learn this - but Erel is a great teacher when you know what to ask!!!
 

Harris

Expert
Licensed User
It's time for me to learn ABMateria
It is time for EVERYONE to learn ABM. Without it, I would not be able to write webapps to save my life!!!
And without B4J, we would not have a fantastic ABM... We are doubly blessed.
 
Last edited:

Harris

Expert
Licensed User
How do you secure the call of the script? Scriptname & Data. No Userid & Pw?
First off, it is great to have a php expert in the camp (1 of many I expect). It is very useful many times. I would not be able to manage my db's without phpmyadmin.

the config.properties (on the server) contains the user and password... It is secure since no outside access is allowed to modify contents... (shown in post above)

The device knows the IP address of your server (you told it).
You know the command names (scripts to call) since you coded them.
All the scripts reside on the server in the same config.properties file.
The IP and port number allows the device to communicate with the RDC running on the server.

The following is from the DBRequestManager class module.

B4X:
'Target - The module that handles JobDone (usually Me).
'ConnectorLink - URL of the Java server.
Public Sub Initialize (Target As Object, ConnectorLink As String)
    mTarget = Target
    link = ConnectorLink
End Sub

'Sends a query request.
'Command - Query name and parameters.
'Limit - Maximum rows to return or 0 for no limit.
'Tag - An object that will be returned in the result.
Public Sub ExecuteQuery(Command As DBCommand, Limit As Int, Tag As Object)
    Dim j As HttpJob
    Dim ms As OutputStream
    Dim out2 As OutputStream = StartJob(j,ms, Tag)
  
    WriteObject(Command.Name, out2)
    WriteInt(Limit, out2)
    WriteList(Command.Parameters, out2)
    out2.Close
    j.PostBytes(link & "?method=query", ms.ToBytesArray)
    Log(" Length of mem stream: "&ms.ToBytesArray.Length&" Tag: "&Tag)
    'ToastMessageShow("Processing Tag: "&Tag, False)

'    LogCM.SetDataUse(Tag,ms.ToBytesArray.Length,0)
  
End Sub

'Executes a batch of (non-select) commands.
'ListOfCommands - List of the commands that will be executes.
'Tag - An object that will be returned in the result.
Public Sub ExecuteBatch(ListOfCommands As List, Tag As Object)
    Dim j As HttpJob
    Dim ms As OutputStream
    Dim out2 As OutputStream = StartJob(j,ms, Tag)
    WriteInt( ListOfCommands.Size, out2)
    For Each Command As DBCommand In ListOfCommands
        WriteObject( Command.Name, out2)
        WriteList(Command.Parameters, out2)
    Next
  
    out2.Close
    j.PostBytes(link & "?method=batch", ms.ToBytesArray)
    Log(" Length of mem stream: "&ms.ToBytesArray.Length&" Tag: "&Tag)
'    LogCM.SetDataUse(Tag,ms.ToBytesArray.Length,0)

End Sub
 
Last edited:

Erel

Administrator
Staff member
Licensed User
The only reason to use PHP is if you are restricted to a shared hosting.

RDC is faster, more powerful and safer. As it is a B4J program you can easily extend it.
It also allows you to use the same B4X programming language for both the backend and frontend.

Note that with jRDC2 the client can read the data asynchronously which is very important if you are working with large data sets.
 

Cableguy

Expert
Licensed User
The only reason to use PHP is if you are restricted to a shared hosting.

RDC is faster, more powerful and safer. As it is a B4J program you can easily extend it.
It also allows you to use the same B4X programming language for both the backend and frontend.

Note that with jRDC2 the client can read the data asynchronously which is very important if you are working with large data sets.
For the simplicity of use I am most inclined to use RDC, but the implementation, for someone as noob as me in these matters, is somewhat confusing... Specially server side
 

udg

Expert
Licensed User
Due to my well-known laziness I didn't try RDC yet, but I did manage to experience some of its benefits using B4J jServer.
On my server I have a MySQL DBMS and a B4J jServer-based webserver (which is the only code accessing the DB).
B4A apps post to the webserver requests like "procedure 35 with parameters X" where X could be a list, a map or any other B4x object usable with B4xSerializator. The webserver replies with similar data structures embedded in a "wrapper" used to communicate eventual error codes or other info.
In my case I even avoided direct use of websockets; everything is managed through handlers' req/resp parameters.

Now, are you willing (please) to give me a couple of good reasons to start experimenting with RDC? Just a mediocre excuse could suffice... eheh
 
Top