diff --git a/B4AHandler.bas b/B4AHandler.bas
deleted file mode 100644
index 901e9cf..0000000
--- a/B4AHandler.bas
+++ /dev/null
@@ -1,319 +0,0 @@
-B4J=true
-Group=Default Group
-ModulesStructureVersion=1
-Type=Class
-Version=4.19
-@EndOfDesignText@
-'Handler class
-Sub Class_Globals
-' #if VERSION1
- Private const T_NULL = 0, T_STRING = 1, T_SHORT = 2, T_INT = 3, T_LONG = 4, T_FLOAT = 5 _
- ,T_DOUBLE = 6, T_BOOLEAN = 7, T_BLOB = 8 As Byte
- Private bc As ByteConverter
- Private cs As CompressedStreams
-' #end if
- Private DateTimeMethods As Map
- Private Connector As RDCConnector
-End Sub
-
-Public Sub Initialize
- DateTimeMethods = CreateMap(91: "getDate", 92: "getTime", 93: "getTimestamp")
-End Sub
-
-Sub Handle(req As ServletRequest, resp As ServletResponse)
- Log("********************* DB1 ********************")
- Dim start As Long = DateTime.Now
- Dim q As String
- Dim in As InputStream = req.InputStream
- Dim method As String = req.GetParameter("method")
- Connector = Main.Connectors.Get("DB1")
- Dim con As SQL
- Try
- con = Connector.GetConnection("DB1")
- If method = "query2" Then
- q = ExecuteQuery2("DB1", con, in, resp)
- '#if VERSION1
- Else if method = "query" Then
- in = cs.WrapInputStream(in, "gzip")
- q = ExecuteQuery("DB1", con, in, resp)
- Else if method = "batch" Then
- in = cs.WrapInputStream(in, "gzip")
- q = ExecuteBatch("DB1", con, in, resp)
- '#end if
- Else if method = "batch2" Then
- q = ExecuteBatch2("DB1", con, in, resp)
- Else
- Log("Unknown method: " & method)
- resp.SendError(500, "unknown method")
- End If
- Catch
- Log(LastException)
- resp.SendError(500, LastException.Message)
- End Try
- If con <> Null And con.IsInitialized Then con.Close
- Log($"Command: ${q}, took: ${DateTime.Now - start}ms, client=${req.RemoteAddress}"$)
-End Sub
-
-Private Sub ExecuteQuery2 (DB As String, 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 cmd As DBCommand = m.Get("command")
- Dim limit As Int = m.Get("limit")
- Dim rs As ResultSet = con.ExecQuery2(Connector.GetCommand(DB, cmd.Name), cmd.Parameters)
- If limit <= 0 Then limit = 0x7fffffff 'max int
- Dim jrs As JavaObject = rs
- Dim rsmd As JavaObject = jrs.RunMethod("getMetaData", Null)
- Dim cols As Int = rs.ColumnCount
- Dim res As DBResult
- res.Initialize
- res.columns.Initialize
- res.Tag = Null 'without this the Tag properly will not be serializable.
- For i = 0 To cols - 1
- res.columns.Put(rs.GetColumnName(i), i)
- Next
- res.Rows.Initialize
- Do While rs.NextRow And limit > 0
- Dim row(cols) As Object
- For i = 0 To cols - 1
- Dim ct As Int = rsmd.RunMethod("getColumnType", Array(i + 1))
- 'check whether it is a blob field
- If ct = -2 Or ct = 2004 Or ct = -3 Or ct = -4 Then
- row(i) = rs.GetBlob2(i)
- Else If ct = 2005 Then
- row(i) = rs.GetString2(i)
- Else if ct = 2 Or ct = 3 Then
- row(i) = rs.GetDouble2(i)
- Else If DateTimeMethods.ContainsKey(ct) Then
- Dim SQLTime As JavaObject = jrs.RunMethodJO(DateTimeMethods.Get(ct), Array(i + 1))
- If SQLTime.IsInitialized Then
- row(i) = SQLTime.RunMethod("getTime", Null)
- Else
- row(i) = Null
- End If
- Else
- row(i) = jrs.RunMethod("getObject", Array(i + 1))
- End If
- Next
- res.Rows.Add(row)
- Loop
- rs.Close
- Dim data() As Byte = ser.ConvertObjectToBytes(res)
- resp.OutputStream.WriteBytes(data, 0, data.Length)
- Return "query: " & cmd.Name
-End Sub
-
-Private Sub ExecuteBatch2(DB As String, 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
- Try
- con.BeginTransaction
- For Each cmd As DBCommand In commands
- con.ExecNonQuery2(Connector.GetCommand(DB, cmd.Name), _
- cmd.Parameters)
- Next
- res.Rows.Add(Array As Object(0))
- con.TransactionSuccessful
- Catch
- con.Rollback
- Log(LastException)
- resp.SendError(500, LastException.Message)
- End Try
- Dim data() As Byte = ser.ConvertObjectToBytes(res)
- resp.OutputStream.WriteBytes(data, 0, data.Length)
- Return $"batch (size=${commands.Size})"$
-End Sub
-
-'#if VERSION1
-
-Private Sub ExecuteBatch(DB As String, con As SQL, in As InputStream, resp As ServletResponse) As String
- Dim clientVersion As Float = ReadObject(in) 'ignore
- Dim numberOfStatements As Int = ReadInt(in)
- Dim res(numberOfStatements) As Int
- Try
- con.BeginTransaction
- For i = 0 To numberOfStatements - 1
- Dim queryName As String = ReadObject(in)
- Dim params As List = ReadList(in)
- con.ExecNonQuery2(Connector.GetCommand(DB, queryName), _
- params)
- Next
- con.TransactionSuccessful
-
- Dim out As OutputStream = cs.WrapOutputStream(resp.OutputStream, "gzip")
- WriteObject(Main.VERSION, out)
- WriteObject("batch", out)
- WriteInt(res.Length, out)
- For Each r As Int In res
- WriteInt(r, out)
- Next
- out.Close
- Catch
- con.Rollback
- Log(LastException)
- resp.SendError(500, LastException.Message)
- End Try
- Return $"batch (size=${numberOfStatements})"$
-End Sub
-
-Private Sub ExecuteQuery(DB As String, con As SQL, in As InputStream, resp As ServletResponse) As String
-' Log("==== ExecuteQuery ==== ")
- Dim clientVersion As Float = ReadObject(in) 'ignore
- Dim queryName As String = ReadObject(in)
- Dim limit As Int = ReadInt(in)
- Dim params As List = ReadList(in)
-' Log("EL QUERY: |" & queryName & "|")
- Private theSql As String = Connector.GetCommand(DB, queryName)
-' Log(theSql)
-' Log(params)
-' Log(params.Size)
- Dim rs As ResultSet = con.ExecQuery2(theSql, params)
- If limit <= 0 Then limit = 0x7fffffff 'max int
- Dim jrs As JavaObject = rs
- Dim rsmd As JavaObject = jrs.RunMethod("getMetaData", Null)
- Dim cols As Int = rs.ColumnCount
- Dim out As OutputStream = cs.WrapOutputStream(resp.OutputStream, "gzip")
- WriteObject(Main.VERSION, out)
- WriteObject("query", out)
- WriteInt(rs.ColumnCount, out)
-' Log($"cols: ${cols}"$)
- For i = 0 To cols - 1
- WriteObject(rs.GetColumnName(i), out)
- Next
-
- Do While rs.NextRow And limit > 0
- WriteByte(1, out)
- For i = 0 To cols - 1
- Dim ct As Int = rsmd.RunMethod("getColumnType", Array(i + 1))
- 'check whether it is a blob field
- If ct = -2 Or ct = 2004 Or ct = -3 Or ct = -4 Then
- WriteObject(rs.GetBlob2(i), out)
- Else
- WriteObject(jrs.RunMethod("getObject", Array(i + 1)), out)
- End If
- Next
- Loop
- WriteByte(0, out)
- out.Close
- rs.Close
-
- Return "query: " & queryName
-End Sub
-
-Private Sub WriteByte(value As Byte, out As OutputStream)
- out.WriteBytes(Array As Byte(value), 0, 1)
-End Sub
-
-Private Sub WriteObject(o As Object, out As OutputStream)
- Dim data() As Byte
- If o = Null Then
- out.WriteBytes(Array As Byte(T_NULL), 0, 1)
- Else If o Is Short Then
- out.WriteBytes(Array As Byte(T_SHORT), 0, 1)
- data = bc.ShortsToBytes(Array As Short(o))
- Else If o Is Int Then
- out.WriteBytes(Array As Byte(T_INT), 0, 1)
- data = bc.IntsToBytes(Array As Int(o))
- Else If o Is Float Then
- out.WriteBytes(Array As Byte(T_FLOAT), 0, 1)
- data = bc.FloatsToBytes(Array As Float(o))
- Else If o Is Double Then
- out.WriteBytes(Array As Byte(T_DOUBLE), 0, 1)
- data = bc.DoublesToBytes(Array As Double(o))
- Else If o Is Long Then
- out.WriteBytes(Array As Byte(T_LONG), 0, 1)
- data = bc.LongsToBytes(Array As Long(o))
- Else If o Is Boolean Then
- out.WriteBytes(Array As Byte(T_BOOLEAN), 0, 1)
- Dim b As Boolean = o
- Dim data(1) As Byte
- If b Then data(0) = 1 Else data(0) = 0
- Else If GetType(o) = "[B" Then
- data = o
- out.WriteBytes(Array As Byte(T_BLOB), 0, 1)
- WriteInt(data.Length, out)
- Else 'If o Is String Then (treat all other values as string)
- out.WriteBytes(Array As Byte(T_STRING), 0, 1)
- data = bc.StringToBytes(o, "UTF8")
- WriteInt(data.Length, out)
- End If
- If data.Length > 0 Then out.WriteBytes(data, 0, data.Length)
-End Sub
-
-Private Sub ReadObject(In As InputStream) As Object
- Dim data(1) As Byte
- In.ReadBytes(data, 0, 1)
- Select data(0)
- Case T_NULL
- Return Null
- Case T_SHORT
- Dim data(2) As Byte
- Return bc.ShortsFromBytes(ReadBytesFully(In, data, data.Length))(0)
- Case T_INT
- Dim data(4) As Byte
- Return bc.IntsFromBytes(ReadBytesFully(In, data, data.Length))(0)
- Case T_LONG
- Dim data(8) As Byte
- Return bc.LongsFromBytes(ReadBytesFully(In, data, data.Length))(0)
- Case T_FLOAT
- Dim data(4) As Byte
- Return bc.FloatsFromBytes(ReadBytesFully(In, data, data.Length))(0)
- Case T_DOUBLE
- Dim data(8) As Byte
- Return bc.DoublesFromBytes(ReadBytesFully(In, data, data.Length))(0)
- Case T_BOOLEAN
- Dim b As Byte = ReadByte(In)
- Return b = 1
- Case T_BLOB
- Dim len As Int = ReadInt(In)
- Dim data(len) As Byte
- Return ReadBytesFully(In, data, data.Length)
- Case Else
- Dim len As Int = ReadInt(In)
- Dim data(len) As Byte
- ReadBytesFully(In, data, data.Length)
- Return BytesToString(data, 0, data.Length, "UTF8")
- End Select
-End Sub
-
-Private Sub ReadBytesFully(In As InputStream, Data() As Byte, Len As Int) As Byte()
- Dim count = 0, Read As Int
- Do While count < Len And Read > -1
- Read = In.ReadBytes(Data, count, Len - count)
- count = count + Read
- Loop
- Return Data
-End Sub
-
-Private Sub WriteInt(i As Int, out As OutputStream)
- Dim data() As Byte
- data = bc.IntsToBytes(Array As Int(i))
- out.WriteBytes(data, 0, data.Length)
-End Sub
-
-Private Sub ReadInt(In As InputStream) As Int
- Dim data(4) As Byte
- Return bc.IntsFromBytes(ReadBytesFully(In, data, data.Length))(0)
-End Sub
-
-Private Sub ReadByte(In As InputStream) As Byte
- Dim data(1) As Byte
- In.ReadBytes(data, 0, 1)
- Return data(0)
-End Sub
-
-Private Sub ReadList(in As InputStream) As List
- Dim len As Int = ReadInt(in)
- Dim l1 As List
- l1.Initialize
- For i = 0 To len - 1
- l1.Add(ReadObject(in))
- Next
- Return l1
-End Sub
-'#end If
\ No newline at end of file
diff --git a/DB1.bas b/DB1.bas
deleted file mode 100644
index f29a0b8..0000000
--- a/DB1.bas
+++ /dev/null
@@ -1,320 +0,0 @@
-B4J=true
-Group=Default Group
-ModulesStructureVersion=1
-Type=Class
-Version=10
-@EndOfDesignText@
-'Handler class
-Sub Class_Globals
-' #if VERSION1
- Private const T_NULL = 0, T_STRING = 1, T_SHORT = 2, T_INT = 3, T_LONG = 4, T_FLOAT = 5 _
- ,T_DOUBLE = 6, T_BOOLEAN = 7, T_BLOB = 8 As Byte
- Private bc As ByteConverter
- Private cs As CompressedStreams
-' #end if
- Private DateTimeMethods As Map
-End Sub
-
-Public Sub Initialize
- DateTimeMethods = CreateMap(91: "getDate", 92: "getTime", 93: "getTimestamp")
-End Sub
-
-Sub Handle(req As ServletRequest, resp As ServletResponse)
- Log("***********************************************")
- Dim start As Long = DateTime.Now
- Dim q As String
- Dim in As InputStream = req.InputStream
- Dim method As String = req.GetParameter("method")
- Dim con As SQL
- Try
- log(">>>>>> " & Main.rdcConnectorDB1.config)
- con = Main.rdcConnectorDB1.GetConnection("DB1")
- If method = "query2" Then
- q = ExecuteQuery2(con, in, resp)
- '#if VERSION1
- Else if method = "query" Then
- in = cs.WrapInputStream(in, "gzip")
- q = ExecuteQuery(con, in, resp)
- Else if method = "batch" Then
- in = cs.WrapInputStream(in, "gzip")
- q = ExecuteBatch(con, in, resp)
- '#end if
- Else if method = "batch2" Then
- q = ExecuteBatch2(con, in, resp)
- Else
- Log("Unknown method: " & method)
- resp.SendError(500, "unknown method")
- End If
- Catch
- Log(LastException)
- resp.SendError(500, LastException.Message)
- End Try
- If con <> Null And con.IsInitialized Then con.Close
- Log($"Command: ${q}, took: ${DateTime.Now - start}ms, client=${req.RemoteAddress}"$)
-End Sub
-
-Private Sub ExecuteQuery2 (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 cmd As DBCommand = m.Get("command")
- Dim limit As Int = m.Get("limit")
- Dim rs As ResultSet = con.ExecQuery2(Main.rdcConnectorDB1.GetCommand(cmd.Name), cmd.Parameters)
- If limit <= 0 Then limit = 0x7fffffff 'max int
- Dim jrs As JavaObject = rs
- Dim rsmd As JavaObject = jrs.RunMethod("getMetaData", Null)
- Dim cols As Int = rs.ColumnCount
- Dim res As DBResult
- res.Initialize
- res.columns.Initialize
- res.Tag = Null 'without this the Tag properly will not be serializable.
- For i = 0 To cols - 1
- res.columns.Put(rs.GetColumnName(i), i)
- Next
- res.Rows.Initialize
- Do While rs.NextRow And limit > 0
- Dim row(cols) As Object
- For i = 0 To cols - 1
- Dim ct As Int = rsmd.RunMethod("getColumnType", Array(i + 1))
- 'check whether it is a blob field
- If ct = -2 Or ct = 2004 Or ct = -3 Or ct = -4 Then
- row(i) = rs.GetBlob2(i)
- Else If ct = 2005 Then
- row(i) = rs.GetString2(i)
- Else if ct = 2 Or ct = 3 Then
- row(i) = rs.GetDouble2(i)
- Else If DateTimeMethods.ContainsKey(ct) Then
- Dim SQLTime As JavaObject = jrs.RunMethodJO(DateTimeMethods.Get(ct), Array(i + 1))
- If SQLTime.IsInitialized Then
- row(i) = SQLTime.RunMethod("getTime", Null)
- Else
- row(i) = Null
- End If
- Else
- row(i) = jrs.RunMethod("getObject", Array(i + 1))
- End If
- Next
- res.Rows.Add(row)
- Loop
- rs.Close
- Dim data() As Byte = ser.ConvertObjectToBytes(res)
- resp.OutputStream.WriteBytes(data, 0, data.Length)
- Return "query: " & cmd.Name
-End Sub
-
-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
- Try
- con.BeginTransaction
- For Each cmd As DBCommand In commands
- con.ExecNonQuery2(Main.rdcConnectorDB1.GetCommand(cmd.Name), _
- cmd.Parameters)
- Next
- res.Rows.Add(Array As Object(0))
- con.TransactionSuccessful
- Catch
- con.Rollback
- Log(LastException)
- resp.SendError(500, LastException.Message)
- End Try
- Dim data() As Byte = ser.ConvertObjectToBytes(res)
- resp.OutputStream.WriteBytes(data, 0, data.Length)
- Return $"batch (size=${commands.Size})"$
-End Sub
-
-'#if VERSION1
-
-Private Sub ExecuteBatch(con As SQL, in As InputStream, resp As ServletResponse) As String
- Dim clientVersion As Float = ReadObject(in) 'ignore
- Dim numberOfStatements As Int = ReadInt(in)
- Dim res(numberOfStatements) As Int
- Try
- con.BeginTransaction
- For i = 0 To numberOfStatements - 1
- Dim queryName As String = ReadObject(in)
- Dim params As List = ReadList(in)
- con.ExecNonQuery2(Main.rdcConnectorDB1.GetCommand(queryName), _
- params)
- Next
- con.TransactionSuccessful
-
- Dim out As OutputStream = cs.WrapOutputStream(resp.OutputStream, "gzip")
- WriteObject(Main.VERSION, out)
- WriteObject("batch", out)
- WriteInt(res.Length, out)
- For Each r As Int In res
- WriteInt(r, out)
- Next
- out.Close
- Catch
- con.Rollback
- Log(LastException)
- resp.SendError(500, LastException.Message)
- End Try
- Return $"batch (size=${numberOfStatements})"$
-End Sub
-
-Private Sub ExecuteQuery (con As SQL, in As InputStream, resp As ServletResponse) As String
-' Log("==== ExecuteQuery ==== ")
- Dim clientVersion As Float = ReadObject(in) 'ignore
- Dim queryName As String = ReadObject(in)
- Dim limit As Int = ReadInt(in)
- Dim params As List = ReadList(in)
-' Log("EL QUERY: |" & queryName & "|")
- Private theSql As String = Main.rdcConnectorDB1.GetCommand(queryName)
-' Log(theSql)
-' Log(params)
-' Log(params.Size)
- Dim rs As ResultSet = con.ExecQuery2(theSql, params)
- If limit <= 0 Then limit = 0x7fffffff 'max int
- Dim jrs As JavaObject = rs
- Dim rsmd As JavaObject = jrs.RunMethod("getMetaData", Null)
- Dim cols As Int = rs.ColumnCount
- Dim out As OutputStream = cs.WrapOutputStream(resp.OutputStream, "gzip")
- WriteObject(Main.VERSION, out)
- WriteObject("query", out)
- WriteInt(rs.ColumnCount, out)
-' Log($"cols: ${cols}"$)
- For i = 0 To cols - 1
- WriteObject(rs.GetColumnName(i), out)
- Next
-
- Do While rs.NextRow And limit > 0
- WriteByte(1, out)
- For i = 0 To cols - 1
- Dim ct As Int = rsmd.RunMethod("getColumnType", Array(i + 1))
- 'check whether it is a blob field
- If ct = -2 Or ct = 2004 Or ct = -3 Or ct = -4 Then
- WriteObject(rs.GetBlob2(i), out)
- Else
- WriteObject(jrs.RunMethod("getObject", Array(i + 1)), out)
- End If
- Next
- Loop
- WriteByte(0, out)
- out.Close
- rs.Close
-
- Return "query: " & queryName
-End Sub
-
-Private Sub WriteByte(value As Byte, out As OutputStream)
- out.WriteBytes(Array As Byte(value), 0, 1)
-End Sub
-
-
-
-Private Sub WriteObject(o As Object, out As OutputStream)
- Dim data() As Byte
- If o = Null Then
- out.WriteBytes(Array As Byte(T_NULL), 0, 1)
- Else If o Is Short Then
- out.WriteBytes(Array As Byte(T_SHORT), 0, 1)
- data = bc.ShortsToBytes(Array As Short(o))
- Else If o Is Int Then
- out.WriteBytes(Array As Byte(T_INT), 0, 1)
- data = bc.IntsToBytes(Array As Int(o))
- Else If o Is Float Then
- out.WriteBytes(Array As Byte(T_FLOAT), 0, 1)
- data = bc.FloatsToBytes(Array As Float(o))
- Else If o Is Double Then
- out.WriteBytes(Array As Byte(T_DOUBLE), 0, 1)
- data = bc.DoublesToBytes(Array As Double(o))
- Else If o Is Long Then
- out.WriteBytes(Array As Byte(T_LONG), 0, 1)
- data = bc.LongsToBytes(Array As Long(o))
- Else If o Is Boolean Then
- out.WriteBytes(Array As Byte(T_BOOLEAN), 0, 1)
- Dim b As Boolean = o
- Dim data(1) As Byte
- If b Then data(0) = 1 Else data(0) = 0
- Else If GetType(o) = "[B" Then
- data = o
- out.WriteBytes(Array As Byte(T_BLOB), 0, 1)
- WriteInt(data.Length, out)
- Else 'If o Is String Then (treat all other values as string)
- out.WriteBytes(Array As Byte(T_STRING), 0, 1)
- data = bc.StringToBytes(o, "UTF8")
- WriteInt(data.Length, out)
- End If
- If data.Length > 0 Then out.WriteBytes(data, 0, data.Length)
-End Sub
-
-Private Sub ReadObject(In As InputStream) As Object
- Dim data(1) As Byte
- In.ReadBytes(data, 0, 1)
- Select data(0)
- Case T_NULL
- Return Null
- Case T_SHORT
- Dim data(2) As Byte
- Return bc.ShortsFromBytes(ReadBytesFully(In, data, data.Length))(0)
- Case T_INT
- Dim data(4) As Byte
- Return bc.IntsFromBytes(ReadBytesFully(In, data, data.Length))(0)
- Case T_LONG
- Dim data(8) As Byte
- Return bc.LongsFromBytes(ReadBytesFully(In, data, data.Length))(0)
- Case T_FLOAT
- Dim data(4) As Byte
- Return bc.FloatsFromBytes(ReadBytesFully(In, data, data.Length))(0)
- Case T_DOUBLE
- Dim data(8) As Byte
- Return bc.DoublesFromBytes(ReadBytesFully(In, data, data.Length))(0)
- Case T_BOOLEAN
- Dim b As Byte = ReadByte(In)
- Return b = 1
- Case T_BLOB
- Dim len As Int = ReadInt(In)
- Dim data(len) As Byte
- Return ReadBytesFully(In, data, data.Length)
- Case Else
- Dim len As Int = ReadInt(In)
- Dim data(len) As Byte
- ReadBytesFully(In, data, data.Length)
- Return BytesToString(data, 0, data.Length, "UTF8")
- End Select
-End Sub
-
-Private Sub ReadBytesFully(In As InputStream, Data() As Byte, Len As Int) As Byte()
- Dim count = 0, Read As Int
- Do While count < Len And Read > -1
- Read = In.ReadBytes(Data, count, Len - count)
- count = count + Read
- Loop
- Return Data
-End Sub
-
-Private Sub WriteInt(i As Int, out As OutputStream)
- Dim data() As Byte
- data = bc.IntsToBytes(Array As Int(i))
- out.WriteBytes(data, 0, data.Length)
-End Sub
-
-Private Sub ReadInt(In As InputStream) As Int
- Dim data(4) As Byte
- Return bc.IntsFromBytes(ReadBytesFully(In, data, data.Length))(0)
-End Sub
-
-Private Sub ReadByte(In As InputStream) As Byte
- Dim data(1) As Byte
- In.ReadBytes(data, 0, 1)
- Return data(0)
-End Sub
-
-Private Sub ReadList(in As InputStream) As List
- Dim len As Int = ReadInt(in)
- Dim l1 As List
- l1.Initialize
- For i = 0 To len - 1
- l1.Add(ReadObject(in))
- Next
- Return l1
-End Sub
-'#end If
\ No newline at end of file
diff --git a/DB1Handler.bas b/DB1Handler.bas
deleted file mode 100644
index 901e9cf..0000000
--- a/DB1Handler.bas
+++ /dev/null
@@ -1,319 +0,0 @@
-B4J=true
-Group=Default Group
-ModulesStructureVersion=1
-Type=Class
-Version=4.19
-@EndOfDesignText@
-'Handler class
-Sub Class_Globals
-' #if VERSION1
- Private const T_NULL = 0, T_STRING = 1, T_SHORT = 2, T_INT = 3, T_LONG = 4, T_FLOAT = 5 _
- ,T_DOUBLE = 6, T_BOOLEAN = 7, T_BLOB = 8 As Byte
- Private bc As ByteConverter
- Private cs As CompressedStreams
-' #end if
- Private DateTimeMethods As Map
- Private Connector As RDCConnector
-End Sub
-
-Public Sub Initialize
- DateTimeMethods = CreateMap(91: "getDate", 92: "getTime", 93: "getTimestamp")
-End Sub
-
-Sub Handle(req As ServletRequest, resp As ServletResponse)
- Log("********************* DB1 ********************")
- Dim start As Long = DateTime.Now
- Dim q As String
- Dim in As InputStream = req.InputStream
- Dim method As String = req.GetParameter("method")
- Connector = Main.Connectors.Get("DB1")
- Dim con As SQL
- Try
- con = Connector.GetConnection("DB1")
- If method = "query2" Then
- q = ExecuteQuery2("DB1", con, in, resp)
- '#if VERSION1
- Else if method = "query" Then
- in = cs.WrapInputStream(in, "gzip")
- q = ExecuteQuery("DB1", con, in, resp)
- Else if method = "batch" Then
- in = cs.WrapInputStream(in, "gzip")
- q = ExecuteBatch("DB1", con, in, resp)
- '#end if
- Else if method = "batch2" Then
- q = ExecuteBatch2("DB1", con, in, resp)
- Else
- Log("Unknown method: " & method)
- resp.SendError(500, "unknown method")
- End If
- Catch
- Log(LastException)
- resp.SendError(500, LastException.Message)
- End Try
- If con <> Null And con.IsInitialized Then con.Close
- Log($"Command: ${q}, took: ${DateTime.Now - start}ms, client=${req.RemoteAddress}"$)
-End Sub
-
-Private Sub ExecuteQuery2 (DB As String, 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 cmd As DBCommand = m.Get("command")
- Dim limit As Int = m.Get("limit")
- Dim rs As ResultSet = con.ExecQuery2(Connector.GetCommand(DB, cmd.Name), cmd.Parameters)
- If limit <= 0 Then limit = 0x7fffffff 'max int
- Dim jrs As JavaObject = rs
- Dim rsmd As JavaObject = jrs.RunMethod("getMetaData", Null)
- Dim cols As Int = rs.ColumnCount
- Dim res As DBResult
- res.Initialize
- res.columns.Initialize
- res.Tag = Null 'without this the Tag properly will not be serializable.
- For i = 0 To cols - 1
- res.columns.Put(rs.GetColumnName(i), i)
- Next
- res.Rows.Initialize
- Do While rs.NextRow And limit > 0
- Dim row(cols) As Object
- For i = 0 To cols - 1
- Dim ct As Int = rsmd.RunMethod("getColumnType", Array(i + 1))
- 'check whether it is a blob field
- If ct = -2 Or ct = 2004 Or ct = -3 Or ct = -4 Then
- row(i) = rs.GetBlob2(i)
- Else If ct = 2005 Then
- row(i) = rs.GetString2(i)
- Else if ct = 2 Or ct = 3 Then
- row(i) = rs.GetDouble2(i)
- Else If DateTimeMethods.ContainsKey(ct) Then
- Dim SQLTime As JavaObject = jrs.RunMethodJO(DateTimeMethods.Get(ct), Array(i + 1))
- If SQLTime.IsInitialized Then
- row(i) = SQLTime.RunMethod("getTime", Null)
- Else
- row(i) = Null
- End If
- Else
- row(i) = jrs.RunMethod("getObject", Array(i + 1))
- End If
- Next
- res.Rows.Add(row)
- Loop
- rs.Close
- Dim data() As Byte = ser.ConvertObjectToBytes(res)
- resp.OutputStream.WriteBytes(data, 0, data.Length)
- Return "query: " & cmd.Name
-End Sub
-
-Private Sub ExecuteBatch2(DB As String, 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
- Try
- con.BeginTransaction
- For Each cmd As DBCommand In commands
- con.ExecNonQuery2(Connector.GetCommand(DB, cmd.Name), _
- cmd.Parameters)
- Next
- res.Rows.Add(Array As Object(0))
- con.TransactionSuccessful
- Catch
- con.Rollback
- Log(LastException)
- resp.SendError(500, LastException.Message)
- End Try
- Dim data() As Byte = ser.ConvertObjectToBytes(res)
- resp.OutputStream.WriteBytes(data, 0, data.Length)
- Return $"batch (size=${commands.Size})"$
-End Sub
-
-'#if VERSION1
-
-Private Sub ExecuteBatch(DB As String, con As SQL, in As InputStream, resp As ServletResponse) As String
- Dim clientVersion As Float = ReadObject(in) 'ignore
- Dim numberOfStatements As Int = ReadInt(in)
- Dim res(numberOfStatements) As Int
- Try
- con.BeginTransaction
- For i = 0 To numberOfStatements - 1
- Dim queryName As String = ReadObject(in)
- Dim params As List = ReadList(in)
- con.ExecNonQuery2(Connector.GetCommand(DB, queryName), _
- params)
- Next
- con.TransactionSuccessful
-
- Dim out As OutputStream = cs.WrapOutputStream(resp.OutputStream, "gzip")
- WriteObject(Main.VERSION, out)
- WriteObject("batch", out)
- WriteInt(res.Length, out)
- For Each r As Int In res
- WriteInt(r, out)
- Next
- out.Close
- Catch
- con.Rollback
- Log(LastException)
- resp.SendError(500, LastException.Message)
- End Try
- Return $"batch (size=${numberOfStatements})"$
-End Sub
-
-Private Sub ExecuteQuery(DB As String, con As SQL, in As InputStream, resp As ServletResponse) As String
-' Log("==== ExecuteQuery ==== ")
- Dim clientVersion As Float = ReadObject(in) 'ignore
- Dim queryName As String = ReadObject(in)
- Dim limit As Int = ReadInt(in)
- Dim params As List = ReadList(in)
-' Log("EL QUERY: |" & queryName & "|")
- Private theSql As String = Connector.GetCommand(DB, queryName)
-' Log(theSql)
-' Log(params)
-' Log(params.Size)
- Dim rs As ResultSet = con.ExecQuery2(theSql, params)
- If limit <= 0 Then limit = 0x7fffffff 'max int
- Dim jrs As JavaObject = rs
- Dim rsmd As JavaObject = jrs.RunMethod("getMetaData", Null)
- Dim cols As Int = rs.ColumnCount
- Dim out As OutputStream = cs.WrapOutputStream(resp.OutputStream, "gzip")
- WriteObject(Main.VERSION, out)
- WriteObject("query", out)
- WriteInt(rs.ColumnCount, out)
-' Log($"cols: ${cols}"$)
- For i = 0 To cols - 1
- WriteObject(rs.GetColumnName(i), out)
- Next
-
- Do While rs.NextRow And limit > 0
- WriteByte(1, out)
- For i = 0 To cols - 1
- Dim ct As Int = rsmd.RunMethod("getColumnType", Array(i + 1))
- 'check whether it is a blob field
- If ct = -2 Or ct = 2004 Or ct = -3 Or ct = -4 Then
- WriteObject(rs.GetBlob2(i), out)
- Else
- WriteObject(jrs.RunMethod("getObject", Array(i + 1)), out)
- End If
- Next
- Loop
- WriteByte(0, out)
- out.Close
- rs.Close
-
- Return "query: " & queryName
-End Sub
-
-Private Sub WriteByte(value As Byte, out As OutputStream)
- out.WriteBytes(Array As Byte(value), 0, 1)
-End Sub
-
-Private Sub WriteObject(o As Object, out As OutputStream)
- Dim data() As Byte
- If o = Null Then
- out.WriteBytes(Array As Byte(T_NULL), 0, 1)
- Else If o Is Short Then
- out.WriteBytes(Array As Byte(T_SHORT), 0, 1)
- data = bc.ShortsToBytes(Array As Short(o))
- Else If o Is Int Then
- out.WriteBytes(Array As Byte(T_INT), 0, 1)
- data = bc.IntsToBytes(Array As Int(o))
- Else If o Is Float Then
- out.WriteBytes(Array As Byte(T_FLOAT), 0, 1)
- data = bc.FloatsToBytes(Array As Float(o))
- Else If o Is Double Then
- out.WriteBytes(Array As Byte(T_DOUBLE), 0, 1)
- data = bc.DoublesToBytes(Array As Double(o))
- Else If o Is Long Then
- out.WriteBytes(Array As Byte(T_LONG), 0, 1)
- data = bc.LongsToBytes(Array As Long(o))
- Else If o Is Boolean Then
- out.WriteBytes(Array As Byte(T_BOOLEAN), 0, 1)
- Dim b As Boolean = o
- Dim data(1) As Byte
- If b Then data(0) = 1 Else data(0) = 0
- Else If GetType(o) = "[B" Then
- data = o
- out.WriteBytes(Array As Byte(T_BLOB), 0, 1)
- WriteInt(data.Length, out)
- Else 'If o Is String Then (treat all other values as string)
- out.WriteBytes(Array As Byte(T_STRING), 0, 1)
- data = bc.StringToBytes(o, "UTF8")
- WriteInt(data.Length, out)
- End If
- If data.Length > 0 Then out.WriteBytes(data, 0, data.Length)
-End Sub
-
-Private Sub ReadObject(In As InputStream) As Object
- Dim data(1) As Byte
- In.ReadBytes(data, 0, 1)
- Select data(0)
- Case T_NULL
- Return Null
- Case T_SHORT
- Dim data(2) As Byte
- Return bc.ShortsFromBytes(ReadBytesFully(In, data, data.Length))(0)
- Case T_INT
- Dim data(4) As Byte
- Return bc.IntsFromBytes(ReadBytesFully(In, data, data.Length))(0)
- Case T_LONG
- Dim data(8) As Byte
- Return bc.LongsFromBytes(ReadBytesFully(In, data, data.Length))(0)
- Case T_FLOAT
- Dim data(4) As Byte
- Return bc.FloatsFromBytes(ReadBytesFully(In, data, data.Length))(0)
- Case T_DOUBLE
- Dim data(8) As Byte
- Return bc.DoublesFromBytes(ReadBytesFully(In, data, data.Length))(0)
- Case T_BOOLEAN
- Dim b As Byte = ReadByte(In)
- Return b = 1
- Case T_BLOB
- Dim len As Int = ReadInt(In)
- Dim data(len) As Byte
- Return ReadBytesFully(In, data, data.Length)
- Case Else
- Dim len As Int = ReadInt(In)
- Dim data(len) As Byte
- ReadBytesFully(In, data, data.Length)
- Return BytesToString(data, 0, data.Length, "UTF8")
- End Select
-End Sub
-
-Private Sub ReadBytesFully(In As InputStream, Data() As Byte, Len As Int) As Byte()
- Dim count = 0, Read As Int
- Do While count < Len And Read > -1
- Read = In.ReadBytes(Data, count, Len - count)
- count = count + Read
- Loop
- Return Data
-End Sub
-
-Private Sub WriteInt(i As Int, out As OutputStream)
- Dim data() As Byte
- data = bc.IntsToBytes(Array As Int(i))
- out.WriteBytes(data, 0, data.Length)
-End Sub
-
-Private Sub ReadInt(In As InputStream) As Int
- Dim data(4) As Byte
- Return bc.IntsFromBytes(ReadBytesFully(In, data, data.Length))(0)
-End Sub
-
-Private Sub ReadByte(In As InputStream) As Byte
- Dim data(1) As Byte
- In.ReadBytes(data, 0, 1)
- Return data(0)
-End Sub
-
-Private Sub ReadList(in As InputStream) As List
- Dim len As Int = ReadInt(in)
- Dim l1 As List
- l1.Initialize
- For i = 0 To len - 1
- l1.Add(ReadObject(in))
- Next
- Return l1
-End Sub
-'#end If
\ No newline at end of file
diff --git a/DB1JsonHandler.bas b/DB1JsonHandler.bas
deleted file mode 100644
index 4adaea3..0000000
--- a/DB1JsonHandler.bas
+++ /dev/null
@@ -1,266 +0,0 @@
-B4J=true
-Group=Default Group
-ModulesStructureVersion=1
-Type=Class
-Version=10.3
-@EndOfDesignText@
-' Handler class for JSON requests from Web Clients (JavaScript/axios)
-' VERSIÓN 16 (Comentarios y Mensajes en Español):
-' - Se añaden comentarios detallados a la versión con mensajes de error en español.
-' - Revisa que el 'query' exista en config.properties antes de continuar.
-' - Asegura que la conexión a la BD se cierre en todos los 'Return' para evitar fugas.
-Sub Class_Globals
- ' Declara una variable privada para mantener una instancia del conector RDC.
- ' Este objeto maneja la comunicación con la base de datos.
- Private Connector As RDCConnector
-End Sub
-
-' Subrutina de inicialización de la clase. Se llama cuando se crea un objeto de esta clase.
-' En este caso, no se necesita ninguna inicialización específica.
-Public Sub Initialize
-
-End Sub
-
-' Este es el método principal que maneja las peticiones HTTP entrantes (req) y prepara la respuesta (resp).
-Sub Handle(req As ServletRequest, resp As ServletResponse)
- Log("============== DB1JsonHandler ==============")
- ' --- Headers CORS (Cross-Origin Resource Sharing) ---
- ' Estos encabezados son necesarios para permitir que un cliente web (ej. una página con JavaScript)
- ' que se encuentra en un dominio diferente pueda hacer peticiones a este servidor.
- resp.SetHeader("Access-Control-Allow-Origin", "*") ' Permite peticiones desde cualquier origen.
- resp.SetHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS") ' Métodos HTTP permitidos.
- resp.SetHeader("Access-Control-Allow-Headers", "Content-Type") ' Encabezados permitidos en la petición.
-
- ' El método OPTIONS es una "petición de comprobación previa" (preflight request) que envían los navegadores
- ' para verificar los permisos CORS antes de enviar la petición real (ej. POST).
- ' Si es una petición OPTIONS, simplemente terminamos la ejecución sin procesar nada más.
- If req.Method = "OPTIONS" Then Return
-
- ' Establece "DB1" como el nombre de la base de datos por defecto.
- Dim DB As String = "DB1"
- ' Obtiene el objeto conector para la base de datos por defecto desde el objeto Main.
- Connector = Main.Connectors.Get(DB)
- ' Declara una variable para la conexión SQL.
- Dim con As SQL
-
- ' Inicia un bloque Try...Catch para manejar posibles errores durante la ejecución.
- Try
- ' Obtiene el valor del parámetro 'j' de la petición. Se espera que contenga una cadena JSON.
- Dim jsonString As String = req.GetParameter("j")
- ' Verifica si el parámetro 'j' es nulo o está vacío.
- If jsonString = Null Or jsonString = "" Then
- ' Si falta el parámetro, envía una respuesta de error 400 (Bad Request) y termina la ejecución.
- SendErrorResponse(resp, 400, "Falta el parametro 'j' en el URL")
- Return
- End If
-
- ' Crea un objeto JSONParser para analizar la cadena JSON.
- Dim parser As JSONParser
- parser.Initialize(jsonString)
- ' Convierte la cadena JSON en un objeto Map, que es como un diccionario (clave-valor).
- Dim RootMap As Map = parser.NextObject
-
- ' Extrae los datos necesarios del JSON.
- Dim execType As String = RootMap.GetDefault("exec", "") ' Tipo de ejecución: "executeQuery" o "executeCommand".
- Dim queryName As String = RootMap.Get("query") ' Nombre del comando SQL (definido en config.properties).
- Dim paramsMap As Map = RootMap.Get("params") ' Un mapa con los parámetros para la consulta.
-' Log(RootMap)
- ' Verifica si en el JSON se especificó un nombre de base de datos diferente con la clave "dbx".
- If RootMap.Get("dbx") <> Null Then DB = RootMap.Get("dbx") ' Si se especifica, usamos la BD indicada, si no, se queda "DB1".
-
- ' Valida que el nombre de la base de datos (DB) exista en la lista de conexiones configuradas en Main.
- If Main.listaDeCP.IndexOf(DB) = -1 Then
- SendErrorResponse(resp, 400, "Parametro 'DB' invalido. El nombre '" & DB & "' no es válido.")
- ' Se añade Return para detener la ejecución si la BD no es válida.
- Return
- End If
-
- ' Prepara una lista para almacenar las claves de los parámetros.
- Dim paramKeys As List
- paramKeys.Initialize
- ' Si el mapa de parámetros existe y está inicializado...
- If paramsMap <> Null And paramsMap.IsInitialized Then
- ' ...itera sobre todas las claves y las añade a la lista 'paramKeys'.
- For Each key As String In paramsMap.Keys
- paramKeys.Add(key)
- Next
- End If
- ' Ordena las claves alfabéticamente. Esto es crucial para asegurar que los parámetros
- ' se pasen a la consulta SQL en un orden consistente y predecible.
- paramKeys.Sort(True)
-
- ' Prepara una lista para almacenar los valores de los parámetros en el orden correcto.
- Dim orderedParams As List
- orderedParams.Initialize
- ' Itera sobre la lista de claves ya ordenada.
- For Each key As String In paramKeys
- ' Añade el valor correspondiente a cada clave a la lista 'orderedParams'.
- orderedParams.Add(paramsMap.Get(key))
- Next
-
- ' Obtiene una conexión a la base de datos del pool de conexiones.
- con = Connector.GetConnection(DB)
- ' Obtiene la cadena SQL del archivo de configuración usando el nombre de la consulta (queryName).
- Dim sqlCommand As String = Connector.GetCommand(DB, queryName)
-
- ' <<< INICIO NUEVA VALIDACIÓN: VERIFICAR SI EL COMANDO EXISTE >>>
- ' Comprueba si el comando SQL (query) especificado en el JSON fue encontrado en el archivo de configuración.
- If sqlCommand = Null Or sqlCommand = "null" Or sqlCommand.Trim = "" Then
- ' Si no se encontró el comando, crea un mensaje de error claro.
- Dim errorMessage As String = $"El comando '${queryName}' no fue encontrado en el config.properties de '${DB}'."$
- ' Registra el error en el log del servidor para depuración.
- Log(errorMessage)
- ' Envía una respuesta de error 400 (Bad Request) al cliente en formato JSON.
- SendErrorResponse(resp, 400, errorMessage)
- ' Cierra la conexión a la base de datos antes de salir para evitar fugas de conexión.
- If con <> Null And con.IsInitialized Then con.Close
- ' Detiene la ejecución del método Handle para esta petición.
- Return
- End If
- ' <<< FIN NUEVA VALIDACIÓN >>>
-
- ' Comprueba el tipo de ejecución solicitado ("executeQuery" o "executeCommand").
- If execType.ToLowerCase = "executequery" Then
- ' Declara una variable para almacenar el resultado de la consulta.
- Dim rs As ResultSet
-
- ' Si el comando SQL contiene placeholders ('?'), significa que espera parámetros.
- If sqlCommand.Contains("?") or orderedParams.Size > 0 Then
- ' =================================================================
- ' === VALIDACIÓN DE CONTEO DE PARÁMETROS ==========================
- ' =================================================================
- ' Calcula cuántos parámetros espera la consulta contando el número de '?'.
- Dim expectedParams As Int = sqlCommand.Length - sqlCommand.Replace("?", "").Length
- ' Obtiene cuántos parámetros se recibieron.
- Dim receivedParams As Int = orderedParams.Size
- ' Compara si la cantidad de parámetros esperados y recibidos es diferente.
-
- Log($"expectedParams: ${expectedParams}, receivedParams: ${receivedParams}"$)
-
- If expectedParams <> receivedParams Then
- ' Si no coinciden, envía un error 400 detallado.
- SendErrorResponse(resp, 400, $"Número de parametros equivocado para '${queryName}'. Se esperaban ${expectedParams} y se recibieron ${receivedParams}."$)
- ' Cierra la conexión antes de salir para evitar fugas.
- If con <> Null And con.IsInitialized Then con.Close
- ' Detiene la ejecución para evitar un error en la base de datos.
- Return
- End If
- ' =================================================================
- ' Ejecuta la consulta pasando el comando SQL y la lista ordenada de parámetros.
- rs = con.ExecQuery2(sqlCommand, orderedParams)
- Else
- ' Si no hay '?', ejecuta la consulta directamente sin parámetros.
- rs = con.ExecQuery(sqlCommand)
- End If
-
- ' --- Procesamiento de resultados ---
- ' Prepara una lista para almacenar todas las filas del resultado.
- Dim ResultList As List
- ResultList.Initialize
- ' Usa un objeto JavaObject para acceder a los metadatos del resultado (info de columnas).
- Dim jrs As JavaObject = rs
- Dim rsmd As JavaObject = jrs.RunMethod("getMetaData", Null)
- ' Obtiene el número de columnas en el resultado.
- Dim cols As Int = rsmd.RunMethod("getColumnCount", Null)
-
- ' Itera sobre cada fila del resultado (ResultSet).
- Do While rs.NextRow
- ' Crea un mapa para almacenar los datos de la fila actual (columna -> valor).
- Dim RowMap As Map
- RowMap.Initialize
- ' Itera sobre cada columna de la fila.
- For i = 1 To cols
- ' Obtiene el nombre de la columna.
- Dim ColumnName As String = rsmd.RunMethod("getColumnName", Array(i))
- ' Obtiene el valor de la columna.
- Dim value As Object = jrs.RunMethod("getObject", Array(i))
- ' Añade la pareja (nombre_columna, valor) al mapa de la fila.
- RowMap.Put(ColumnName, value)
- Next
- ' Añade el mapa de la fila a la lista de resultados.
- ResultList.Add(RowMap)
- Loop
- ' Cierra el ResultSet para liberar recursos de la base de datos.
- rs.Close
-
- ' Envía una respuesta de éxito con la lista de resultados en formato JSON.
- SendSuccessResponse(resp, CreateMap("result": ResultList))
-
- Else If execType.ToLowerCase = "executecommand" Then
- ' Si es un comando (INSERT, UPDATE, DELETE), también valida los parámetros.
- If sqlCommand.Contains("?") Then
- ' =================================================================
- ' === VALIDACIÓN DE CONTEO DE PARÁMETROS (para Comandos) ==========
- ' =================================================================
- Dim expectedParams As Int = sqlCommand.Length - sqlCommand.Replace("?", "").Length
- Dim receivedParams As Int = orderedParams.Size
- If expectedParams <> receivedParams Then
- SendErrorResponse(resp, 400, $"Número de parametros equivocado para '${queryName}'. Se esperaban ${expectedParams} y se recibieron ${receivedParams}."$)
- ' Cierra la conexión antes de salir.
- If con <> Null And con.IsInitialized Then con.Close
- ' Detiene la ejecución.
- Return
- End If
- ' =================================================================
- End If
-
- ' Ejecuta el comando que no devuelve resultados (NonQuery) con sus parámetros.
- con.ExecNonQuery2(sqlCommand, orderedParams)
- ' Envía una respuesta de éxito con un mensaje de confirmación.
- SendSuccessResponse(resp, CreateMap("message": "Command executed successfully"))
-
- Else
- ' Si el valor de 'exec' no es ni "executeQuery" ni "executeCommand", envía un error.
- SendErrorResponse(resp, 400, "Parametro 'exec' inválido. '" & execType & "' no es un valor permitido.")
- End If
-
- Catch
- ' Si ocurre cualquier error inesperado en el bloque Try...
- ' Registra la excepción completa en el log del servidor para diagnóstico.
- Log(LastException)
- ' Envía una respuesta de error 500 (Internal Server Error) con el mensaje de la excepción.
- SendErrorResponse(resp, 500, LastException.Message)
- End Try
-
- ' Este bloque se ejecuta siempre al final, haya habido error o no, *excepto si se usó Return antes*.
- ' Comprueba si el objeto de conexión fue inicializado y sigue abierto.
- If con <> Null And con.IsInitialized Then
- ' Cierra la conexión para devolverla al pool y que pueda ser reutilizada.
- ' Esto es fundamental para no agotar las conexiones a la base de datos.
- con.Close
- End If
-End Sub
-
-
-' --- Subrutinas de ayuda para respuestas JSON ---
-
-' Construye y envía una respuesta JSON de éxito.
-Private Sub SendSuccessResponse(resp As ServletResponse, dataMap As Map)
- ' Añade el campo "success": true al mapa de datos para indicar que todo salió bien.
- dataMap.Put("success", True)
- ' Crea un generador de JSON.
- Dim jsonGenerator As JSONGenerator
- jsonGenerator.Initialize(dataMap)
- ' Establece el tipo de contenido de la respuesta a "application/json".
- resp.ContentType = "application/json"
- ' Escribe la cadena JSON generada en el cuerpo de la respuesta HTTP.
- resp.Write(jsonGenerator.ToString)
-End Sub
-
-' Construye y envía una respuesta JSON de error.
-Private Sub SendErrorResponse(resp As ServletResponse, statusCode As Int, errorMessage As String)
- ' Personaliza el mensaje de error si es un error común de parámetros de Oracle o JDBC.
- If errorMessage.Contains("Índice de columnas no válido") Or errorMessage.Contains("ORA-17003") Then errorMessage = "NUMERO DE PARAMETROS EQUIVOCADO: " & errorMessage
- ' Crea un mapa con el estado de error y el mensaje.
- Dim resMap As Map = CreateMap("success": False, "error": errorMessage)
- ' Genera la cadena JSON a partir del mapa.
- Dim jsonGenerator As JSONGenerator
- jsonGenerator.Initialize(resMap)
- ' Establece el código de estado HTTP (ej. 400 para error del cliente, 500 para error del servidor).
- resp.Status = statusCode
- ' Establece el tipo de contenido y escribe la respuesta de error.
- resp.ContentType = "application/json"
- resp.Write(jsonGenerator.ToString)
-End Sub
-
-
diff --git a/DB2Handler.bas b/DB2Handler.bas
deleted file mode 100644
index 4562230..0000000
--- a/DB2Handler.bas
+++ /dev/null
@@ -1,320 +0,0 @@
-B4J=true
-Group=Default Group
-ModulesStructureVersion=1
-Type=Class
-Version=10
-@EndOfDesignText@
-'Handler class
-Sub Class_Globals
-' #if VERSION1
- Private const T_NULL = 0, T_STRING = 1, T_SHORT = 2, T_INT = 3, T_LONG = 4, T_FLOAT = 5 _
- ,T_DOUBLE = 6, T_BOOLEAN = 7, T_BLOB = 8 As Byte
- Private bc As ByteConverter
- Private cs As CompressedStreams
-' #end if
- Private DateTimeMethods As Map
- Private Connector As RDCConnector
-End Sub
-
-Public Sub Initialize
- DateTimeMethods = CreateMap(91: "getDate", 92: "getTime", 93: "getTimestamp")
-End Sub
-
-Sub Handle(req As ServletRequest, resp As ServletResponse)
- Log("********************* DB2 ********************")
- Dim start As Long = DateTime.Now
- Dim q As String
- Dim in As InputStream = req.InputStream
- Dim method As String = req.GetParameter("method")
- Connector = Main.Connectors.Get("DB2")
- Dim con As SQL
- Try
- con = Connector.GetConnection("DB2")
- If method = "query2" Then
- q = ExecuteQuery2("DB2", con, in, resp)
- '#if VERSION1
- Else if method = "query" Then
- in = cs.WrapInputStream(in, "gzip")
- q = ExecuteQuery("DB2", con, in, resp)
- Else if method = "batch" Then
- in = cs.WrapInputStream(in, "gzip")
- q = ExecuteBatch("DB2", con, in, resp)
- '#end if
- Else if method = "batch2" Then
- q = ExecuteBatch2("DB2", con, in, resp)
- Else
- Log("Unknown method: " & method)
- resp.SendError(500, "unknown method")
- End If
- Catch
- Log(LastException)
- resp.SendError(500, LastException.Message)
- End Try
- If con <> Null And con.IsInitialized Then con.Close
- Log($"Command: ${q}, took: ${DateTime.Now - start}ms, client=${req.RemoteAddress}"$)
-End Sub
-
-Private Sub ExecuteQuery2 (DB As String, con As SQL, in As InputStream, resp As ServletResponse) As String
- Log("==== ExecuteQuery2 ==== ")
- Dim ser As B4XSerializator
- Dim m As Map = ser.ConvertBytesToObject(Bit.InputStreamToBytes(in))
- Dim cmd As DBCommand = m.Get("command")
- Dim limit As Int = m.Get("limit")
- Dim rs As ResultSet = con.ExecQuery2(Connector.GetCommand(DB, cmd.Name), cmd.Parameters)
- If limit <= 0 Then limit = 0x7fffffff 'max int
- Dim jrs As JavaObject = rs
- Dim rsmd As JavaObject = jrs.RunMethod("getMetaData", Null)
- Dim cols As Int = rs.ColumnCount
- Dim res As DBResult
- res.Initialize
- res.columns.Initialize
- res.Tag = Null 'without this the Tag properly will not be serializable.
- For i = 0 To cols - 1
- res.columns.Put(rs.GetColumnName(i), i)
- Next
- res.Rows.Initialize
- Do While rs.NextRow And limit > 0
- Dim row(cols) As Object
- For i = 0 To cols - 1
- Dim ct As Int = rsmd.RunMethod("getColumnType", Array(i + 1))
- 'check whether it is a blob field
- If ct = -2 Or ct = 2004 Or ct = -3 Or ct = -4 Then
- row(i) = rs.GetBlob2(i)
- Else If ct = 2005 Then
- row(i) = rs.GetString2(i)
- Else if ct = 2 Or ct = 3 Then
- row(i) = rs.GetDouble2(i)
- Else If DateTimeMethods.ContainsKey(ct) Then
- Dim SQLTime As JavaObject = jrs.RunMethodJO(DateTimeMethods.Get(ct), Array(i + 1))
- If SQLTime.IsInitialized Then
- row(i) = SQLTime.RunMethod("getTime", Null)
- Else
- row(i) = Null
- End If
- Else
- row(i) = jrs.RunMethod("getObject", Array(i + 1))
- End If
- Next
- res.Rows.Add(row)
- Loop
- rs.Close
- Dim data() As Byte = ser.ConvertObjectToBytes(res)
- resp.OutputStream.WriteBytes(data, 0, data.Length)
- Return "query: " & cmd.Name
-End Sub
-
-Private Sub ExecuteBatch2(DB As String, 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
- Try
- con.BeginTransaction
- For Each cmd As DBCommand In commands
- con.ExecNonQuery2(Connector.GetCommand(DB, cmd.Name), _
- cmd.Parameters)
- Next
- res.Rows.Add(Array As Object(0))
- con.TransactionSuccessful
- Catch
- con.Rollback
- Log(LastException)
- resp.SendError(500, LastException.Message)
- End Try
- Dim data() As Byte = ser.ConvertObjectToBytes(res)
- resp.OutputStream.WriteBytes(data, 0, data.Length)
- Return $"batch (size=${commands.Size})"$
-End Sub
-
-'#if VERSION1
-
-Private Sub ExecuteBatch(DB As String, con As SQL, in As InputStream, resp As ServletResponse) As String
- Dim clientVersion As Float = ReadObject(in) 'ignore
- Dim numberOfStatements As Int = ReadInt(in)
- Dim res(numberOfStatements) As Int
- Try
- con.BeginTransaction
- For i = 0 To numberOfStatements - 1
- Dim queryName As String = ReadObject(in)
- Dim params As List = ReadList(in)
- con.ExecNonQuery2(Connector.GetCommand(DB, queryName), _
- params)
- Next
- con.TransactionSuccessful
-
- Dim out As OutputStream = cs.WrapOutputStream(resp.OutputStream, "gzip")
- WriteObject(Main.VERSION, out)
- WriteObject("batch", out)
- WriteInt(res.Length, out)
- For Each r As Int In res
- WriteInt(r, out)
- Next
- out.Close
- Catch
- con.Rollback
- Log(LastException)
- resp.SendError(500, LastException.Message)
- End Try
- Return $"batch (size=${numberOfStatements})"$
-End Sub
-
-Private Sub ExecuteQuery (DB As String, con As SQL, in As InputStream, resp As ServletResponse) As String
- Log("==== ExecuteQuery ==== ")
- Dim clientVersion As Float = ReadObject(in) 'ignore
- Dim queryName As String = ReadObject(in)
- Dim limit As Int = ReadInt(in)
- Dim params As List = ReadList(in)
-' Log("EL QUERY: |" & queryName & "|")
- Private theSql As String = Connector.GetCommand(DB, queryName)
-' Log(theSql)
-' Log(params)
-' Log(params.Size)
- Dim rs As ResultSet = con.ExecQuery2(theSql, params)
- If limit <= 0 Then limit = 0x7fffffff 'max int
- Dim jrs As JavaObject = rs
- Dim rsmd As JavaObject = jrs.RunMethod("getMetaData", Null)
- Dim cols As Int = rs.ColumnCount
- Dim out As OutputStream = cs.WrapOutputStream(resp.OutputStream, "gzip")
- WriteObject(Main.VERSION, out)
- WriteObject("query", out)
- WriteInt(rs.ColumnCount, out)
-' Log($"cols: ${cols}"$)
- For i = 0 To cols - 1
- WriteObject(rs.GetColumnName(i), out)
- Next
-
- Do While rs.NextRow And limit > 0
- WriteByte(1, out)
- For i = 0 To cols - 1
- Dim ct As Int = rsmd.RunMethod("getColumnType", Array(i + 1))
- 'check whether it is a blob field
- If ct = -2 Or ct = 2004 Or ct = -3 Or ct = -4 Then
- WriteObject(rs.GetBlob2(i), out)
- Else
- WriteObject(jrs.RunMethod("getObject", Array(i + 1)), out)
- End If
- Next
- Loop
- WriteByte(0, out)
- out.Close
- rs.Close
-
- Return "query: " & queryName
-End Sub
-
-Private Sub WriteByte(value As Byte, out As OutputStream)
- out.WriteBytes(Array As Byte(value), 0, 1)
-End Sub
-
-Private Sub WriteObject(o As Object, out As OutputStream)
- Dim data() As Byte
- If o = Null Then
- out.WriteBytes(Array As Byte(T_NULL), 0, 1)
- Else If o Is Short Then
- out.WriteBytes(Array As Byte(T_SHORT), 0, 1)
- data = bc.ShortsToBytes(Array As Short(o))
- Else If o Is Int Then
- out.WriteBytes(Array As Byte(T_INT), 0, 1)
- data = bc.IntsToBytes(Array As Int(o))
- Else If o Is Float Then
- out.WriteBytes(Array As Byte(T_FLOAT), 0, 1)
- data = bc.FloatsToBytes(Array As Float(o))
- Else If o Is Double Then
- out.WriteBytes(Array As Byte(T_DOUBLE), 0, 1)
- data = bc.DoublesToBytes(Array As Double(o))
- Else If o Is Long Then
- out.WriteBytes(Array As Byte(T_LONG), 0, 1)
- data = bc.LongsToBytes(Array As Long(o))
- Else If o Is Boolean Then
- out.WriteBytes(Array As Byte(T_BOOLEAN), 0, 1)
- Dim b As Boolean = o
- Dim data(1) As Byte
- If b Then data(0) = 1 Else data(0) = 0
- Else If GetType(o) = "[B" Then
- data = o
- out.WriteBytes(Array As Byte(T_BLOB), 0, 1)
- WriteInt(data.Length, out)
- Else 'If o Is String Then (treat all other values as string)
- out.WriteBytes(Array As Byte(T_STRING), 0, 1)
- data = bc.StringToBytes(o, "UTF8")
- WriteInt(data.Length, out)
- End If
- If data.Length > 0 Then out.WriteBytes(data, 0, data.Length)
-End Sub
-
-Private Sub ReadObject(In As InputStream) As Object
- Dim data(1) As Byte
- In.ReadBytes(data, 0, 1)
- Select data(0)
- Case T_NULL
- Return Null
- Case T_SHORT
- Dim data(2) As Byte
- Return bc.ShortsFromBytes(ReadBytesFully(In, data, data.Length))(0)
- Case T_INT
- Dim data(4) As Byte
- Return bc.IntsFromBytes(ReadBytesFully(In, data, data.Length))(0)
- Case T_LONG
- Dim data(8) As Byte
- Return bc.LongsFromBytes(ReadBytesFully(In, data, data.Length))(0)
- Case T_FLOAT
- Dim data(4) As Byte
- Return bc.FloatsFromBytes(ReadBytesFully(In, data, data.Length))(0)
- Case T_DOUBLE
- Dim data(8) As Byte
- Return bc.DoublesFromBytes(ReadBytesFully(In, data, data.Length))(0)
- Case T_BOOLEAN
- Dim b As Byte = ReadByte(In)
- Return b = 1
- Case T_BLOB
- Dim len As Int = ReadInt(In)
- Dim data(len) As Byte
- Return ReadBytesFully(In, data, data.Length)
- Case Else
- Dim len As Int = ReadInt(In)
- Dim data(len) As Byte
- ReadBytesFully(In, data, data.Length)
- Return BytesToString(data, 0, data.Length, "UTF8")
- End Select
-End Sub
-
-Private Sub ReadBytesFully(In As InputStream, Data() As Byte, Len As Int) As Byte()
- Dim count = 0, Read As Int
- Do While count < Len And Read > -1
- Read = In.ReadBytes(Data, count, Len - count)
- count = count + Read
- Loop
- Return Data
-End Sub
-
-Private Sub WriteInt(i As Int, out As OutputStream)
- Dim data() As Byte
- data = bc.IntsToBytes(Array As Int(i))
- out.WriteBytes(data, 0, data.Length)
-End Sub
-
-Private Sub ReadInt(In As InputStream) As Int
- Dim data(4) As Byte
- Return bc.IntsFromBytes(ReadBytesFully(In, data, data.Length))(0)
-End Sub
-
-Private Sub ReadByte(In As InputStream) As Byte
- Dim data(1) As Byte
- In.ReadBytes(data, 0, 1)
- Return data(0)
-End Sub
-
-Private Sub ReadList(in As InputStream) As List
- Dim len As Int = ReadInt(in)
- Dim l1 As List
- l1.Initialize
- For i = 0 To len - 1
- l1.Add(ReadObject(in))
- Next
- Return l1
-End Sub
-'#end If
\ No newline at end of file
diff --git a/DB3Handler.bas b/DB3Handler.bas
deleted file mode 100644
index e07a2dc..0000000
--- a/DB3Handler.bas
+++ /dev/null
@@ -1,319 +0,0 @@
-B4J=true
-Group=Default Group
-ModulesStructureVersion=1
-Type=Class
-Version=10
-@EndOfDesignText@
-'Handler class
-Sub Class_Globals
-' #if VERSION1
- Private const T_NULL = 0, T_STRING = 1, T_SHORT = 2, T_INT = 3, T_LONG = 4, T_FLOAT = 5 _
- ,T_DOUBLE = 6, T_BOOLEAN = 7, T_BLOB = 8 As Byte
- Private bc As ByteConverter
- Private cs As CompressedStreams
-' #end if
- Private DateTimeMethods As Map
- Private Connector As RDCConnector
-End Sub
-
-Public Sub Initialize
- DateTimeMethods = CreateMap(91: "getDate", 92: "getTime", 93: "getTimestamp")
-End Sub
-
-Sub Handle(req As ServletRequest, resp As ServletResponse)
- Log("********************* DB3 ********************")
- Dim start As Long = DateTime.Now
- Dim q As String
- Dim in As InputStream = req.InputStream
- Dim method As String = req.GetParameter("method")
- Connector = Main.Connectors.Get("DB3")
- Dim con As SQL
- Try
- con = Connector.GetConnection("DB3")
- If method = "query2" Then
- q = ExecuteQuery2("DB3", con, in, resp)
- '#if VERSION1
- Else if method = "query" Then
- in = cs.WrapInputStream(in, "gzip")
- q = ExecuteQuery("DB3", con, in, resp)
- Else if method = "batch" Then
- in = cs.WrapInputStream(in, "gzip")
- q = ExecuteBatch("DB3", con, in, resp)
- '#end if
- Else if method = "batch2" Then
- q = ExecuteBatch2("DB3", con, in, resp)
- Else
- Log("Unknown method: " & method)
- resp.SendError(500, "unknown method")
- End If
- Catch
- Log(LastException)
- resp.SendError(500, LastException.Message)
- End Try
- If con <> Null And con.IsInitialized Then con.Close
- Log($"Command: ${q}, took: ${DateTime.Now - start}ms, client=${req.RemoteAddress}"$)
-End Sub
-
-Private Sub ExecuteQuery2 (DB As String, 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 cmd As DBCommand = m.Get("command")
- Dim limit As Int = m.Get("limit")
- Dim rs As ResultSet = con.ExecQuery2(Connector.GetCommand(DB, cmd.Name), cmd.Parameters)
- If limit <= 0 Then limit = 0x7fffffff 'max int
- Dim jrs As JavaObject = rs
- Dim rsmd As JavaObject = jrs.RunMethod("getMetaData", Null)
- Dim cols As Int = rs.ColumnCount
- Dim res As DBResult
- res.Initialize
- res.columns.Initialize
- res.Tag = Null 'without this the Tag properly will not be serializable.
- For i = 0 To cols - 1
- res.columns.Put(rs.GetColumnName(i), i)
- Next
- res.Rows.Initialize
- Do While rs.NextRow And limit > 0
- Dim row(cols) As Object
- For i = 0 To cols - 1
- Dim ct As Int = rsmd.RunMethod("getColumnType", Array(i + 1))
- 'check whether it is a blob field
- If ct = -2 Or ct = 2004 Or ct = -3 Or ct = -4 Then
- row(i) = rs.GetBlob2(i)
- Else If ct = 2005 Then
- row(i) = rs.GetString2(i)
- Else if ct = 2 Or ct = 3 Then
- row(i) = rs.GetDouble2(i)
- Else If DateTimeMethods.ContainsKey(ct) Then
- Dim SQLTime As JavaObject = jrs.RunMethodJO(DateTimeMethods.Get(ct), Array(i + 1))
- If SQLTime.IsInitialized Then
- row(i) = SQLTime.RunMethod("getTime", Null)
- Else
- row(i) = Null
- End If
- Else
- row(i) = jrs.RunMethod("getObject", Array(i + 1))
- End If
- Next
- res.Rows.Add(row)
- Loop
- rs.Close
- Dim data() As Byte = ser.ConvertObjectToBytes(res)
- resp.OutputStream.WriteBytes(data, 0, data.Length)
- Return "query: " & cmd.Name
-End Sub
-
-Private Sub ExecuteBatch2(DB As String, 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
- Try
- con.BeginTransaction
- For Each cmd As DBCommand In commands
- con.ExecNonQuery2(Connector.GetCommand(DB, cmd.Name), _
- cmd.Parameters)
- Next
- res.Rows.Add(Array As Object(0))
- con.TransactionSuccessful
- Catch
- con.Rollback
- Log(LastException)
- resp.SendError(500, LastException.Message)
- End Try
- Dim data() As Byte = ser.ConvertObjectToBytes(res)
- resp.OutputStream.WriteBytes(data, 0, data.Length)
- Return $"batch (size=${commands.Size})"$
-End Sub
-
-'#if VERSION1
-
-Private Sub ExecuteBatch(DB As String, con As SQL, in As InputStream, resp As ServletResponse) As String
- Dim clientVersion As Float = ReadObject(in) 'ignore
- Dim numberOfStatements As Int = ReadInt(in)
- Dim res(numberOfStatements) As Int
- Try
- con.BeginTransaction
- For i = 0 To numberOfStatements - 1
- Dim queryName As String = ReadObject(in)
- Dim params As List = ReadList(in)
- con.ExecNonQuery2(Connector.GetCommand(DB, queryName), _
- params)
- Next
- con.TransactionSuccessful
-
- Dim out As OutputStream = cs.WrapOutputStream(resp.OutputStream, "gzip")
- WriteObject(Main.VERSION, out)
- WriteObject("batch", out)
- WriteInt(res.Length, out)
- For Each r As Int In res
- WriteInt(r, out)
- Next
- out.Close
- Catch
- con.Rollback
- Log(LastException)
- resp.SendError(500, LastException.Message)
- End Try
- Return $"batch (size=${numberOfStatements})"$
-End Sub
-
-Private Sub ExecuteQuery (DB As String, con As SQL, in As InputStream, resp As ServletResponse) As String
-' Log("==== ExecuteQuery ==== ")
- Dim clientVersion As Float = ReadObject(in) 'ignore
- Dim queryName As String = ReadObject(in)
- Dim limit As Int = ReadInt(in)
- Dim params As List = ReadList(in)
-' Log("EL QUERY: |" & queryName & "|")
- Private theSql As String = Connector.GetCommand(DB, queryName)
-' Log(theSql)
-' Log(params)
-' Log(params.Size)
- Dim rs As ResultSet = con.ExecQuery2(theSql, params)
- If limit <= 0 Then limit = 0x7fffffff 'max int
- Dim jrs As JavaObject = rs
- Dim rsmd As JavaObject = jrs.RunMethod("getMetaData", Null)
- Dim cols As Int = rs.ColumnCount
- Dim out As OutputStream = cs.WrapOutputStream(resp.OutputStream, "gzip")
- WriteObject(Main.VERSION, out)
- WriteObject("query", out)
- WriteInt(rs.ColumnCount, out)
-' Log($"cols: ${cols}"$)
- For i = 0 To cols - 1
- WriteObject(rs.GetColumnName(i), out)
- Next
-
- Do While rs.NextRow And limit > 0
- WriteByte(1, out)
- For i = 0 To cols - 1
- Dim ct As Int = rsmd.RunMethod("getColumnType", Array(i + 1))
- 'check whether it is a blob field
- If ct = -2 Or ct = 2004 Or ct = -3 Or ct = -4 Then
- WriteObject(rs.GetBlob2(i), out)
- Else
- WriteObject(jrs.RunMethod("getObject", Array(i + 1)), out)
- End If
- Next
- Loop
- WriteByte(0, out)
- out.Close
- rs.Close
-
- Return "query: " & queryName
-End Sub
-
-Private Sub WriteByte(value As Byte, out As OutputStream)
- out.WriteBytes(Array As Byte(value), 0, 1)
-End Sub
-
-Private Sub WriteObject(o As Object, out As OutputStream)
- Dim data() As Byte
- If o = Null Then
- out.WriteBytes(Array As Byte(T_NULL), 0, 1)
- Else If o Is Short Then
- out.WriteBytes(Array As Byte(T_SHORT), 0, 1)
- data = bc.ShortsToBytes(Array As Short(o))
- Else If o Is Int Then
- out.WriteBytes(Array As Byte(T_INT), 0, 1)
- data = bc.IntsToBytes(Array As Int(o))
- Else If o Is Float Then
- out.WriteBytes(Array As Byte(T_FLOAT), 0, 1)
- data = bc.FloatsToBytes(Array As Float(o))
- Else If o Is Double Then
- out.WriteBytes(Array As Byte(T_DOUBLE), 0, 1)
- data = bc.DoublesToBytes(Array As Double(o))
- Else If o Is Long Then
- out.WriteBytes(Array As Byte(T_LONG), 0, 1)
- data = bc.LongsToBytes(Array As Long(o))
- Else If o Is Boolean Then
- out.WriteBytes(Array As Byte(T_BOOLEAN), 0, 1)
- Dim b As Boolean = o
- Dim data(1) As Byte
- If b Then data(0) = 1 Else data(0) = 0
- Else If GetType(o) = "[B" Then
- data = o
- out.WriteBytes(Array As Byte(T_BLOB), 0, 1)
- WriteInt(data.Length, out)
- Else 'If o Is String Then (treat all other values as string)
- out.WriteBytes(Array As Byte(T_STRING), 0, 1)
- data = bc.StringToBytes(o, "UTF8")
- WriteInt(data.Length, out)
- End If
- If data.Length > 0 Then out.WriteBytes(data, 0, data.Length)
-End Sub
-
-Private Sub ReadObject(In As InputStream) As Object
- Dim data(1) As Byte
- In.ReadBytes(data, 0, 1)
- Select data(0)
- Case T_NULL
- Return Null
- Case T_SHORT
- Dim data(2) As Byte
- Return bc.ShortsFromBytes(ReadBytesFully(In, data, data.Length))(0)
- Case T_INT
- Dim data(4) As Byte
- Return bc.IntsFromBytes(ReadBytesFully(In, data, data.Length))(0)
- Case T_LONG
- Dim data(8) As Byte
- Return bc.LongsFromBytes(ReadBytesFully(In, data, data.Length))(0)
- Case T_FLOAT
- Dim data(4) As Byte
- Return bc.FloatsFromBytes(ReadBytesFully(In, data, data.Length))(0)
- Case T_DOUBLE
- Dim data(8) As Byte
- Return bc.DoublesFromBytes(ReadBytesFully(In, data, data.Length))(0)
- Case T_BOOLEAN
- Dim b As Byte = ReadByte(In)
- Return b = 1
- Case T_BLOB
- Dim len As Int = ReadInt(In)
- Dim data(len) As Byte
- Return ReadBytesFully(In, data, data.Length)
- Case Else
- Dim len As Int = ReadInt(In)
- Dim data(len) As Byte
- ReadBytesFully(In, data, data.Length)
- Return BytesToString(data, 0, data.Length, "UTF8")
- End Select
-End Sub
-
-Private Sub ReadBytesFully(In As InputStream, Data() As Byte, Len As Int) As Byte()
- Dim count = 0, Read As Int
- Do While count < Len And Read > -1
- Read = In.ReadBytes(Data, count, Len - count)
- count = count + Read
- Loop
- Return Data
-End Sub
-
-Private Sub WriteInt(i As Int, out As OutputStream)
- Dim data() As Byte
- data = bc.IntsToBytes(Array As Int(i))
- out.WriteBytes(data, 0, data.Length)
-End Sub
-
-Private Sub ReadInt(In As InputStream) As Int
- Dim data(4) As Byte
- Return bc.IntsFromBytes(ReadBytesFully(In, data, data.Length))(0)
-End Sub
-
-Private Sub ReadByte(In As InputStream) As Byte
- Dim data(1) As Byte
- In.ReadBytes(data, 0, 1)
- Return data(0)
-End Sub
-
-Private Sub ReadList(in As InputStream) As List
- Dim len As Int = ReadInt(in)
- Dim l1 As List
- l1.Initialize
- For i = 0 To len - 1
- l1.Add(ReadObject(in))
- Next
- Return l1
-End Sub
-'#end If
\ No newline at end of file
diff --git a/DB4Handler.bas b/DB4Handler.bas
deleted file mode 100644
index bc2ea24..0000000
--- a/DB4Handler.bas
+++ /dev/null
@@ -1,319 +0,0 @@
-B4J=true
-Group=Default Group
-ModulesStructureVersion=1
-Type=Class
-Version=10
-@EndOfDesignText@
-'Handler class
-Sub Class_Globals
-' #if VERSION1
- Private const T_NULL = 0, T_STRING = 1, T_SHORT = 2, T_INT = 3, T_LONG = 4, T_FLOAT = 5 _
- ,T_DOUBLE = 6, T_BOOLEAN = 7, T_BLOB = 8 As Byte
- Private bc As ByteConverter
- Private cs As CompressedStreams
-' #end if
- Private DateTimeMethods As Map
- Private Connector As RDCConnector
-End Sub
-
-Public Sub Initialize
- DateTimeMethods = CreateMap(91: "getDate", 92: "getTime", 93: "getTimestamp")
-End Sub
-
-Sub Handle(req As ServletRequest, resp As ServletResponse)
- Log("********************* DB4 ********************")
- Dim start As Long = DateTime.Now
- Dim q As String
- Dim in As InputStream = req.InputStream
- Dim method As String = req.GetParameter("method")
- Connector = Main.Connectors.Get("DB4")
- Dim con As SQL
- Try
- con = Connector.GetConnection("DB4")
- If method = "query2" Then
- q = ExecuteQuery2("DB4", con, in, resp)
- '#if VERSION1
- Else if method = "query" Then
- in = cs.WrapInputStream(in, "gzip")
- q = ExecuteQuery("DB4", con, in, resp)
- Else if method = "batch" Then
- in = cs.WrapInputStream(in, "gzip")
- q = ExecuteBatch("DB4", con, in, resp)
- '#end if
- Else if method = "batch2" Then
- q = ExecuteBatch2("DB4", con, in, resp)
- Else
- Log("Unknown method: " & method)
- resp.SendError(500, "unknown method")
- End If
- Catch
- Log(LastException)
- resp.SendError(500, LastException.Message)
- End Try
- If con <> Null And con.IsInitialized Then con.Close
- Log($"Command: ${q}, took: ${DateTime.Now - start}ms, client=${req.RemoteAddress}"$)
-End Sub
-
-Private Sub ExecuteQuery2 (DB As String, 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 cmd As DBCommand = m.Get("command")
- Dim limit As Int = m.Get("limit")
- Dim rs As ResultSet = con.ExecQuery2(Connector.GetCommand(DB, cmd.Name), cmd.Parameters)
- If limit <= 0 Then limit = 0x7fffffff 'max int
- Dim jrs As JavaObject = rs
- Dim rsmd As JavaObject = jrs.RunMethod("getMetaData", Null)
- Dim cols As Int = rs.ColumnCount
- Dim res As DBResult
- res.Initialize
- res.columns.Initialize
- res.Tag = Null 'without this the Tag properly will not be serializable.
- For i = 0 To cols - 1
- res.columns.Put(rs.GetColumnName(i), i)
- Next
- res.Rows.Initialize
- Do While rs.NextRow And limit > 0
- Dim row(cols) As Object
- For i = 0 To cols - 1
- Dim ct As Int = rsmd.RunMethod("getColumnType", Array(i + 1))
- 'check whether it is a blob field
- If ct = -2 Or ct = 2004 Or ct = -3 Or ct = -4 Then
- row(i) = rs.GetBlob2(i)
- Else If ct = 2005 Then
- row(i) = rs.GetString2(i)
- Else if ct = 2 Or ct = 3 Then
- row(i) = rs.GetDouble2(i)
- Else If DateTimeMethods.ContainsKey(ct) Then
- Dim SQLTime As JavaObject = jrs.RunMethodJO(DateTimeMethods.Get(ct), Array(i + 1))
- If SQLTime.IsInitialized Then
- row(i) = SQLTime.RunMethod("getTime", Null)
- Else
- row(i) = Null
- End If
- Else
- row(i) = jrs.RunMethod("getObject", Array(i + 1))
- End If
- Next
- res.Rows.Add(row)
- Loop
- rs.Close
- Dim data() As Byte = ser.ConvertObjectToBytes(res)
- resp.OutputStream.WriteBytes(data, 0, data.Length)
- Return "query: " & cmd.Name
-End Sub
-
-Private Sub ExecuteBatch2(DB As String, 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
- Try
- con.BeginTransaction
- For Each cmd As DBCommand In commands
- con.ExecNonQuery2(Connector.GetCommand(DB, cmd.Name), _
- cmd.Parameters)
- Next
- res.Rows.Add(Array As Object(0))
- con.TransactionSuccessful
- Catch
- con.Rollback
- Log(LastException)
- resp.SendError(500, LastException.Message)
- End Try
- Dim data() As Byte = ser.ConvertObjectToBytes(res)
- resp.OutputStream.WriteBytes(data, 0, data.Length)
- Return $"batch (size=${commands.Size})"$
-End Sub
-
-'#if VERSION1
-
-Private Sub ExecuteBatch(DB As String, con As SQL, in As InputStream, resp As ServletResponse) As String
- Dim clientVersion As Float = ReadObject(in) 'ignore
- Dim numberOfStatements As Int = ReadInt(in)
- Dim res(numberOfStatements) As Int
- Try
- con.BeginTransaction
- For i = 0 To numberOfStatements - 1
- Dim queryName As String = ReadObject(in)
- Dim params As List = ReadList(in)
- con.ExecNonQuery2(Connector.GetCommand(DB, queryName), _
- params)
- Next
- con.TransactionSuccessful
-
- Dim out As OutputStream = cs.WrapOutputStream(resp.OutputStream, "gzip")
- WriteObject(Main.VERSION, out)
- WriteObject("batch", out)
- WriteInt(res.Length, out)
- For Each r As Int In res
- WriteInt(r, out)
- Next
- out.Close
- Catch
- con.Rollback
- Log(LastException)
- resp.SendError(500, LastException.Message)
- End Try
- Return $"batch (size=${numberOfStatements})"$
-End Sub
-
-Private Sub ExecuteQuery (DB As String, con As SQL, in As InputStream, resp As ServletResponse) As String
-' Log("==== ExecuteQuery ==== ")
- Dim clientVersion As Float = ReadObject(in) 'ignore
- Dim queryName As String = ReadObject(in)
- Dim limit As Int = ReadInt(in)
- Dim params As List = ReadList(in)
-' Log("EL QUERY: |" & queryName & "|")
- Private theSql As String = Connector.GetCommand(DB, queryName)
-' Log(theSql)
-' Log(params)
-' Log(params.Size)
- Dim rs As ResultSet = con.ExecQuery2(theSql, params)
- If limit <= 0 Then limit = 0x7fffffff 'max int
- Dim jrs As JavaObject = rs
- Dim rsmd As JavaObject = jrs.RunMethod("getMetaData", Null)
- Dim cols As Int = rs.ColumnCount
- Dim out As OutputStream = cs.WrapOutputStream(resp.OutputStream, "gzip")
- WriteObject(Main.VERSION, out)
- WriteObject("query", out)
- WriteInt(rs.ColumnCount, out)
-' Log($"cols: ${cols}"$)
- For i = 0 To cols - 1
- WriteObject(rs.GetColumnName(i), out)
- Next
-
- Do While rs.NextRow And limit > 0
- WriteByte(1, out)
- For i = 0 To cols - 1
- Dim ct As Int = rsmd.RunMethod("getColumnType", Array(i + 1))
- 'check whether it is a blob field
- If ct = -2 Or ct = 2004 Or ct = -3 Or ct = -4 Then
- WriteObject(rs.GetBlob2(i), out)
- Else
- WriteObject(jrs.RunMethod("getObject", Array(i + 1)), out)
- End If
- Next
- Loop
- WriteByte(0, out)
- out.Close
- rs.Close
-
- Return "query: " & queryName
-End Sub
-
-Private Sub WriteByte(value As Byte, out As OutputStream)
- out.WriteBytes(Array As Byte(value), 0, 1)
-End Sub
-
-Private Sub WriteObject(o As Object, out As OutputStream)
- Dim data() As Byte
- If o = Null Then
- out.WriteBytes(Array As Byte(T_NULL), 0, 1)
- Else If o Is Short Then
- out.WriteBytes(Array As Byte(T_SHORT), 0, 1)
- data = bc.ShortsToBytes(Array As Short(o))
- Else If o Is Int Then
- out.WriteBytes(Array As Byte(T_INT), 0, 1)
- data = bc.IntsToBytes(Array As Int(o))
- Else If o Is Float Then
- out.WriteBytes(Array As Byte(T_FLOAT), 0, 1)
- data = bc.FloatsToBytes(Array As Float(o))
- Else If o Is Double Then
- out.WriteBytes(Array As Byte(T_DOUBLE), 0, 1)
- data = bc.DoublesToBytes(Array As Double(o))
- Else If o Is Long Then
- out.WriteBytes(Array As Byte(T_LONG), 0, 1)
- data = bc.LongsToBytes(Array As Long(o))
- Else If o Is Boolean Then
- out.WriteBytes(Array As Byte(T_BOOLEAN), 0, 1)
- Dim b As Boolean = o
- Dim data(1) As Byte
- If b Then data(0) = 1 Else data(0) = 0
- Else If GetType(o) = "[B" Then
- data = o
- out.WriteBytes(Array As Byte(T_BLOB), 0, 1)
- WriteInt(data.Length, out)
- Else 'If o Is String Then (treat all other values as string)
- out.WriteBytes(Array As Byte(T_STRING), 0, 1)
- data = bc.StringToBytes(o, "UTF8")
- WriteInt(data.Length, out)
- End If
- If data.Length > 0 Then out.WriteBytes(data, 0, data.Length)
-End Sub
-
-Private Sub ReadObject(In As InputStream) As Object
- Dim data(1) As Byte
- In.ReadBytes(data, 0, 1)
- Select data(0)
- Case T_NULL
- Return Null
- Case T_SHORT
- Dim data(2) As Byte
- Return bc.ShortsFromBytes(ReadBytesFully(In, data, data.Length))(0)
- Case T_INT
- Dim data(4) As Byte
- Return bc.IntsFromBytes(ReadBytesFully(In, data, data.Length))(0)
- Case T_LONG
- Dim data(8) As Byte
- Return bc.LongsFromBytes(ReadBytesFully(In, data, data.Length))(0)
- Case T_FLOAT
- Dim data(4) As Byte
- Return bc.FloatsFromBytes(ReadBytesFully(In, data, data.Length))(0)
- Case T_DOUBLE
- Dim data(8) As Byte
- Return bc.DoublesFromBytes(ReadBytesFully(In, data, data.Length))(0)
- Case T_BOOLEAN
- Dim b As Byte = ReadByte(In)
- Return b = 1
- Case T_BLOB
- Dim len As Int = ReadInt(In)
- Dim data(len) As Byte
- Return ReadBytesFully(In, data, data.Length)
- Case Else
- Dim len As Int = ReadInt(In)
- Dim data(len) As Byte
- ReadBytesFully(In, data, data.Length)
- Return BytesToString(data, 0, data.Length, "UTF8")
- End Select
-End Sub
-
-Private Sub ReadBytesFully(In As InputStream, Data() As Byte, Len As Int) As Byte()
- Dim count = 0, Read As Int
- Do While count < Len And Read > -1
- Read = In.ReadBytes(Data, count, Len - count)
- count = count + Read
- Loop
- Return Data
-End Sub
-
-Private Sub WriteInt(i As Int, out As OutputStream)
- Dim data() As Byte
- data = bc.IntsToBytes(Array As Int(i))
- out.WriteBytes(data, 0, data.Length)
-End Sub
-
-Private Sub ReadInt(In As InputStream) As Int
- Dim data(4) As Byte
- Return bc.IntsFromBytes(ReadBytesFully(In, data, data.Length))(0)
-End Sub
-
-Private Sub ReadByte(In As InputStream) As Byte
- Dim data(1) As Byte
- In.ReadBytes(data, 0, 1)
- Return data(0)
-End Sub
-
-Private Sub ReadList(in As InputStream) As List
- Dim len As Int = ReadInt(in)
- Dim l1 As List
- l1.Initialize
- For i = 0 To len - 1
- l1.Add(ReadObject(in))
- Next
- Return l1
-End Sub
-'#end If
\ No newline at end of file
diff --git a/DBHandlerGenerico.bas b/DBHandlerB4X.bas
similarity index 100%
rename from DBHandlerGenerico.bas
rename to DBHandlerB4X.bas
diff --git a/Files/reiniciaProcesoPM2.bat b/Files/reiniciaProcesoPM2.bat
index dc1bd70..d9912fe 100644
--- a/Files/reiniciaProcesoPM2.bat
+++ b/Files/reiniciaProcesoPM2.bat
@@ -4,6 +4,6 @@
set "params=%*"
cd /d "%~dp0" && ( if exist "%temp%\getadmin.vbs" del "%temp%\getadmin.vbs" ) && fsutil dirty query %systemdrive% 1>nul 2>nul || ( echo Set UAC = CreateObject^("Shell.Application"^) : UAC.ShellExecute "cmd.exe", "/k cd ""%~sdp0"" && ""%~s0"" %params%", "", "runas", 1 >> "%temp%\getadmin.vbs" && "%temp%\getadmin.vbs" && exit /B )
-pm2 start RDC-Multi
+pm2 restart jRDC-Multi
exit
\ No newline at end of file
diff --git a/HandlerB4X.bas b/HandlerB4X.bas
deleted file mode 100644
index b7b2dfb..0000000
--- a/HandlerB4X.bas
+++ /dev/null
@@ -1,609 +0,0 @@
-B4J=true
-Group=Default Group
-ModulesStructureVersion=1
-Type=Class
-Version=10.3
-@EndOfDesignText@
-' Handler genérico para peticiones desde clientes B4A/B4i (DBRequestManager)
-' Determina la base de datos a utilizar dinámicamente a partir de la URL de la petición.
-' Versión con validación de parámetros y errores en texto plano.
-Sub Class_Globals
- ' Estas constantes y variables solo se compilan si se usa la #if VERSION1,
- ' lo que sugiere que es para dar soporte a una versión antigua del protocolo de comunicación.
-' #if VERSION1
- ' Constantes para identificar los tipos de datos en la serialización personalizada (protocolo V1).
- Private const T_NULL = 0, T_STRING = 1, T_SHORT = 2, T_INT = 3, T_LONG = 4, T_FLOAT = 5 _
- ,T_DOUBLE = 6, T_BOOLEAN = 7, T_BLOB = 8 As Byte
- ' Utilidades para convertir entre tipos de datos y arrays de bytes.
- Private bc As ByteConverter
- ' Utilidad para comprimir/descomprimir streams de datos (usado en V1).
- Private cs As CompressedStreams
-' #end if
-
- ' Mapa para convertir tipos de columna JDBC de fecha/hora a métodos de obtención de datos.
- Private DateTimeMethods As Map
- ' Objeto que gestiona las conexiones a las diferentes bases de datos definidas en config.properties.
- Private Connector As RDCConnector
-End Sub
-
-' Se ejecuta una vez cuando se crea una instancia de esta clase.
-Public Sub Initialize
- ' Inicializa el mapa que asocia los códigos de tipo de columna de fecha/hora de JDBC
- ' con los nombres de los métodos correspondientes para leerlos correctamente.
- DateTimeMethods = CreateMap(91: "getDate", 92: "getTime", 93: "getTimestamp")
-End Sub
-
-' Método principal que maneja cada petición HTTP que llega a este servlet.
-Sub Handle(req As ServletRequest, resp As ServletResponse)
- ' === INICIO DE LA LÓGICA DINÁMICA ===
- ' Extrae la URI completa de la petición (ej. /DB1/endpoint).
- Dim URI As String = req.RequestURI
- ' Variable para almacenar la "llave" o identificador de la base de datos (ej. "DB1").
- Dim dbKey As String
-
- ' Comprueba si la URI tiene contenido y empieza con "/".
- If URI.Length > 1 And URI.StartsWith("/") Then
- ' Extrae la parte de la URI que viene después del primer "/".
- dbKey = URI.Substring(1)
- ' Si la llave contiene más "/", se queda solo con la primera parte.
- ' Esto permite URLs como /DB1/clientes o /DB2/productos, extrayendo "DB1" o "DB2".
- If dbKey.Contains("/") Then
- dbKey = dbKey.SubString2(0, dbKey.IndexOf("/"))
- End If
- Else
- ' Si la URI está vacía o es "/", usa "DB1" como la base de datos por defecto.
- dbKey = "DB1"
- End If
-
- ' Convierte la llave a mayúsculas para que no sea sensible a mayúsculas/minúsculas (ej. "db1" se convierte en "DB1").
- dbKey = dbKey.ToUpperCase
-
- ' Verifica si la llave de la base de datos extraída existe en la configuración de conectores.
- If Main.Connectors.ContainsKey(dbKey) = False Then
- ' Si no existe, crea un mensaje de error claro.
- Dim ErrorMsg As String = $"Invalid DB key specified in URL: '${dbKey}'. Valid keys are: ${Main.listaDeCP}"$
- ' Registra el error en el log del servidor.
- Log(ErrorMsg)
- ' Envía una respuesta de error 400 (Bad Request) al cliente en formato de texto plano.
- SendPlainTextError(resp, 400, ErrorMsg)
- ' Termina la ejecución de este método.
- Return
- End If
- ' === FIN DE LA LÓGICA DINÁMICA ===
-
- Log("********************* " & dbKey & " ********************")
- ' Guarda el tiempo de inicio para medir la duración de la petición.
- Dim start As Long = DateTime.Now
- ' Variable para almacenar el nombre del comando SQL a ejecutar.
- Dim q As String
- ' Obtiene el stream de entrada de la petición, que contiene los datos enviados por el cliente.
- Dim in As InputStream = req.InputStream
- ' Obtiene el parámetro "method" de la URL (ej. ?method=query2).
- Dim method As String = req.GetParameter("method")
- ' Obtiene el conector correspondiente a la base de datos seleccionada.
- Connector = Main.Connectors.Get(dbKey)
- ' Declara la variable para la conexión a la base de datos.
- Dim con As SQL
- Try
- ' Obtiene una conexión del pool de conexiones.
- con = Connector.GetConnection(dbKey)
- Log("Metodo: " & method)
- ' Determina qué función ejecutar basándose en el parámetro "method".
- If method = "query2" Then
- ' Ejecuta una consulta usando el protocolo más nuevo (B4XSerializator).
- q = ExecuteQuery2(dbKey, con, in, resp)
- '#if VERSION1
- Else if method = "query" Then
- ' Protocolo antiguo: descomprime el stream y ejecuta la consulta.
- in = cs.WrapInputStream(in, "gzip")
- q = ExecuteQuery(dbKey, con, in, resp)
- Else if method = "batch" Then
- ' Protocolo antiguo: descomprime el stream y ejecuta un lote de comandos.
- in = cs.WrapInputStream(in, "gzip")
- q = ExecuteBatch(dbKey, con, in, resp)
- '#end if
- Else if method = "batch2" Then
- ' Ejecuta un lote de comandos usando el protocolo más nuevo.
- q = ExecuteBatch2(dbKey, con, in, resp)
- Else
- ' Si el método es desconocido, lo registra y envía un error.
- Log("Unknown method: " & method)
- SendPlainTextError(resp, 500, "unknown method")
- End If
- Catch
- ' Si ocurre cualquier error en el bloque Try, lo captura.
- Log(LastException)
- ' Envía un error 500 (Internal Server Error) al cliente con el mensaje de la excepción.
- SendPlainTextError(resp, 500, LastException.Message)
- End Try
- ' Asegura que la conexión a la BD se cierre y se devuelva al pool.
- If con <> Null And con.IsInitialized Then con.Close
- ' Registra en el log el comando ejecutado, cuánto tiempo tardó y la IP del cliente.
- Log($"Command: ${q}, took: ${DateTime.Now - start}ms, client=${req.RemoteAddress}"$)
-End Sub
-
-' Ejecuta una consulta única usando el protocolo V2 (B4XSerializator).
-Private Sub ExecuteQuery2 (DB As String, con As SQL, in As InputStream, resp As ServletResponse) As String
- ' Objeto para deserializar los datos enviados desde el cliente.
- Dim ser As B4XSerializator
- ' Convierte el stream de entrada a un array de bytes y luego a un objeto Mapa.
- Dim m As Map = ser.ConvertBytesToObject(Bit.InputStreamToBytes(in))
- ' Extrae el objeto DBCommand del mapa.
- Dim cmd As DBCommand = m.Get("command")
- ' Extrae el límite de filas a devolver.
- Dim limit As Int = m.Get("limit")
-
- ' Obtiene la sentencia SQL correspondiente al nombre del comando desde config.properties.
- Dim sqlCommand As String = Connector.GetCommand(DB, cmd.Name)
-
- ' <<< INICIO NUEVA VALIDACIÓN: VERIFICAR SI EL COMANDO EXISTE >>>
- ' Comprueba si el comando no fue encontrado en el archivo de configuración.
- If sqlCommand = Null Or sqlCommand = "null" Or sqlCommand.Trim = "" Then
- Dim errorMessage As String = $"El comando '${cmd.Name}' no fue encontrado en el config.properties de '${DB}'."$
- Log(errorMessage)
- ' Envía un error 400 (Bad Request) al cliente informando del problema.
- SendPlainTextError(resp, 400, errorMessage)
- Return "error" ' Retorna un texto para el log.
- End If
- ' <<< FIN NUEVA VALIDACIÓN >>>
-
- ' --- INICIO VALIDACIÓN DE PARÁMETROS ---
- ' Comprueba si el SQL espera parámetros o si se recibieron parámetros.
- If sqlCommand.Contains("?") Or (cmd.Parameters <> Null And cmd.Parameters.Length > 0) Then
- ' Cuenta cuántos '?' hay en la sentencia SQL para saber cuántos parámetros se esperan.
- Dim expectedParams As Int = sqlCommand.Length - sqlCommand.Replace("?", "").Length
- ' Cuenta cuántos parámetros se recibieron.
- Dim receivedParams As Int
- If cmd.Parameters = Null Then receivedParams = 0 Else receivedParams = cmd.Parameters.Length
-
- ' Compara el número de parámetros esperados con los recibidos.
- If expectedParams <> receivedParams Then
- Dim errorMessage As String = $"Número de parametros equivocado para "${cmd.Name}". Se esperaban ${expectedParams} y se recibieron ${receivedParams}."$
- Log(errorMessage)
- ' Si no coinciden, envía un error 400 al cliente.
- SendPlainTextError(resp, 400, errorMessage)
- Return "error"
- End If
- End If
- ' --- FIN VALIDACIÓN ---
-
- ' Ejecuta la consulta SQL con los parámetros proporcionados.
- Dim rs As ResultSet = con.ExecQuery2(sqlCommand, cmd.Parameters)
- ' Si el límite es 0 o negativo, lo establece a un valor muy alto (máximo entero).
- If limit <= 0 Then limit = 0x7fffffff 'max int
- ' Obtiene el objeto Java subyacente del ResultSet para acceder a métodos adicionales.
- Dim jrs As JavaObject = rs
- ' Obtiene los metadatos del ResultSet (información sobre las columnas).
- Dim rsmd As JavaObject = jrs.RunMethod("getMetaData", Null)
- ' Obtiene el número de columnas del resultado.
- Dim cols As Int = rs.ColumnCount
- ' Crea un objeto DBResult para empaquetar la respuesta.
- Dim res As DBResult
- res.Initialize
- res.columns.Initialize
- res.Tag = Null
- ' Llena el mapa de columnas con el nombre de cada columna y su índice.
- For i = 0 To cols - 1
- res.columns.Put(rs.GetColumnName(i), i)
- Next
- ' Inicializa la lista de filas.
- res.Rows.Initialize
- ' Itera sobre cada fila del ResultSet, hasta llegar al límite.
- Do While rs.NextRow And limit > 0
- Dim row(cols) As Object
- ' Itera sobre cada columna de la fila actual.
- For i = 0 To cols - 1
- ' Obtiene el tipo de dato de la columna según JDBC.
- Dim ct As Int = rsmd.RunMethod("getColumnType", Array(i + 1))
- ' Maneja diferentes tipos de datos para leerlos de la forma correcta.
- If ct = -2 Or ct = 2004 Or ct = -3 Or ct = -4 Then ' Tipos BLOB/binarios
- row(i) = rs.GetBlob2(i)
- Else If ct = 2005 Then ' Tipo CLOB (texto largo)
- row(i) = rs.GetString2(i)
- Else if ct = 2 Or ct = 3 Then ' Tipos numéricos que pueden tener decimales
- row(i) = rs.GetDouble2(i)
- Else If DateTimeMethods.ContainsKey(ct) Then ' Tipos de Fecha/Hora
- ' Obtiene el objeto de tiempo/fecha de Java.
- Dim SQLTime As JavaObject = jrs.RunMethodJO(DateTimeMethods.Get(ct), Array(i + 1))
- If SQLTime.IsInitialized Then
- ' Lo convierte a milisegundos (Long) para B4X.
- row(i) = SQLTime.RunMethod("getTime", Null)
- Else
- row(i) = Null
- End If
- Else ' Para todos los demás tipos de datos
- ' Usa getObject que funciona para la mayoría de los tipos estándar.
- row(i) = jrs.RunMethod("getObject", Array(i + 1))
- End If
- Next
- ' Añade la fila completa a la lista de resultados.
- res.Rows.Add(row)
- limit = limit - 1
- Loop
- ' Cierra el ResultSet para liberar recursos.
- rs.Close
- ' Serializa el objeto DBResult completo a un array de bytes.
- Dim data() As Byte = ser.ConvertObjectToBytes(res)
- ' Escribe los datos serializados en el stream de respuesta.
- resp.OutputStream.WriteBytes(data, 0, data.Length)
- ' Devuelve el nombre del comando para el log.
- Return "query: " & cmd.Name
-End Sub
-
-' Ejecuta un lote de comandos (INSERT, UPDATE, DELETE) usando el protocolo V2.
-Private Sub ExecuteBatch2(DB As String, con As SQL, in As InputStream, resp As ServletResponse) As String
- Dim ser As B4XSerializator
- ' Deserializa el mapa que contiene la lista de comandos.
- Dim m As Map = ser.ConvertBytesToObject(Bit.InputStreamToBytes(in))
- ' Obtiene la lista de objetos DBCommand.
- Dim commands As List = m.Get("commands")
- ' Prepara un objeto DBResult para la respuesta (aunque para batch no devuelve datos, solo confirmación).
- Dim res As DBResult
- res.Initialize
- res.columns = CreateMap("AffectedRows (N/A)": 0)
- res.Rows.Initialize
- res.Tag = Null
- Try
- ' Inicia una transacción. Todos los comandos del lote se ejecutarán como una unidad.
- con.BeginTransaction
- ' Itera sobre cada comando en la lista.
- For Each cmd As DBCommand In commands
- ' Obtiene la sentencia SQL para el comando actual.
- Dim sqlCommand As String = Connector.GetCommand(DB, cmd.Name)
-
- ' <<< INICIO NUEVA VALIDACIÓN: VERIFICAR SI EL COMANDO EXISTE DENTRO DEL BATCH >>>
- If sqlCommand = Null Or sqlCommand = "null" Or sqlCommand.Trim = "" Then
- con.Rollback ' Deshace la transacción si un comando es inválido.
- Dim errorMessage As String = $"El comando '${cmd.Name}' no fue encontrado en el config.properties de '${DB}'."$
- Log(errorMessage)
- SendPlainTextError(resp, 400, errorMessage)
- Return "error"
- End If
- ' <<< FIN NUEVA VALIDACIÓN >>>
-
- ' --- INICIO VALIDACIÓN DE PARÁMETROS DENTRO DEL BATCH ---
- If sqlCommand.Contains("?") Or (cmd.Parameters <> Null And cmd.Parameters.Length > 0) Then
- Dim expectedParams As Int = sqlCommand.Length - sqlCommand.Replace("?", "").Length
- Dim receivedParams As Int
- If cmd.Parameters = Null Then receivedParams = 0 Else receivedParams = cmd.Parameters.Length
-
- ' Si el número de parámetros no coincide, deshace la transacción y envía error.
- If expectedParams <> receivedParams Then
- con.Rollback
- Dim errorMessage As String = $"Número de parametros equivocado para "${cmd.Name}". Se esperaban ${expectedParams} y se recibieron ${receivedParams}."$
- Log(errorMessage)
- SendPlainTextError(resp, 400, errorMessage)
- Return "error"
- End If
- End If
- ' --- FIN VALIDACIÓN ---
-
- ' Ejecuta el comando (no es una consulta, no devuelve filas).
- con.ExecNonQuery2(sqlCommand, cmd.Parameters)
- Next
- ' Añade una fila simbólica al resultado para indicar éxito.
- res.Rows.Add(Array As Object(0))
- ' Si todos los comandos se ejecutaron sin error, confirma la transacción.
- con.TransactionSuccessful
- Catch
- ' Si cualquier comando falla, se captura el error.
- con.Rollback ' Se deshacen todos los cambios hechos en la transacción.
- Log(LastException)
- SendPlainTextError(resp, 500, LastException.Message)
- End Try
- ' Serializa y envía la respuesta al cliente.
- Dim data() As Byte = ser.ConvertObjectToBytes(res)
- resp.OutputStream.WriteBytes(data, 0, data.Length)
- ' Devuelve un resumen para el log.
- Return $"batch (size=${commands.Size})"$
-End Sub
-
-' Código compilado condicionalmente para el protocolo antiguo (V1).
-'#if VERSION1
-
-' Ejecuta un lote de comandos usando el protocolo V1.
-Private Sub ExecuteBatch(DB As String, con As SQL, in As InputStream, resp As ServletResponse) As String
- ' Lee y descarta la versión del cliente.
- Dim clientVersion As Float = ReadObject(in) 'ignore
- ' Lee cuántos comandos vienen en el lote.
- Dim numberOfStatements As Int = ReadInt(in)
- Dim res(numberOfStatements) As Int ' Array para resultados (aunque no se usa).
- Try
- con.BeginTransaction
- ' Itera para procesar cada comando del lote.
- For i = 0 To numberOfStatements - 1
- ' Lee el nombre del comando y la lista de parámetros usando el deserializador V1.
- Dim queryName As String = ReadObject(in)
- Dim params As List = ReadList(in)
-
- Dim sqlCommand As String = Connector.GetCommand(DB, queryName)
-
- ' <<< INICIO NUEVA VALIDACIÓN: VERIFICAR SI EL COMANDO EXISTE (V1) >>>
- If sqlCommand = Null Or sqlCommand = "null" Or sqlCommand.Trim = "" Then
- con.Rollback
- Dim errorMessage As String = $"El comando '${queryName}' no fue encontrado en el config.properties de '${DB}'."$
- Log(errorMessage)
- SendPlainTextError(resp, 400, errorMessage)
- Return "error"
- End If
- ' <<< FIN NUEVA VALIDACIÓN >>>
-
- ' --- INICIO VALIDACIÓN DE PARÁMETROS DENTRO DEL BATCH (V1) ---
- If sqlCommand.Contains("?") Or (params <> Null And params.Size > 0) Then
- Dim expectedParams As Int = sqlCommand.Length - sqlCommand.Replace("?", "").Length
- Dim receivedParams As Int
- If params = Null Then receivedParams = 0 Else receivedParams = params.Size
-
- If expectedParams <> receivedParams Then
- con.Rollback
- Dim errorMessage As String = $"Número de parametros equivocado para "${queryName}". Se esperaban ${expectedParams} y se recibieron ${receivedParams}."$
- Log(errorMessage)
- SendPlainTextError(resp, 400, errorMessage)
- Return "error"
- End If
- End If
- ' --- FIN VALIDACIÓN ---
-
- ' Ejecuta el comando.
- con.ExecNonQuery2(sqlCommand, params)
- Next
- ' Confirma la transacción.
- con.TransactionSuccessful
-
- ' Comprime la salida antes de enviarla.
- Dim out As OutputStream = cs.WrapOutputStream(resp.OutputStream, "gzip")
- ' Escribe la respuesta usando el serializador V1.
- WriteObject(Main.VERSION, out)
- WriteObject("batch", out)
- WriteInt(res.Length, out)
- For Each r As Int In res
- WriteInt(r, out)
- Next
- out.Close
- Catch
- con.Rollback
- Log(LastException)
- SendPlainTextError(resp, 500, LastException.Message)
- End Try
- Return $"batch (size=${numberOfStatements})"$
-End Sub
-
-' Ejecuta una consulta única usando el protocolo V1.
-Private Sub ExecuteQuery(DB As String, con As SQL, in As InputStream, resp As ServletResponse) As String
- Log("====================== ExecuteQuery =====================")
- ' Deserializa los datos de la petición usando el protocolo V1.
- Dim clientVersion As Float = ReadObject(in) 'ignore
- Dim queryName As String = ReadObject(in)
- Dim limit As Int = ReadInt(in)
- Dim params As List = ReadList(in)
- ' Obtiene la sentencia SQL.
- Dim theSql As String = Connector.GetCommand(DB, queryName)
-' Log(444 & "|" & theSql)
-
- ' <<< INICIO NUEVA VALIDACIÓN: VERIFICAR SI EL COMANDO EXISTE (V1) >>>
- If theSql = Null Or theSql ="null" Or theSql.Trim = "" Then
- Dim errorMessage As String = $"El comando '${queryName}' no fue encontrado en el config.properties de '${DB}'."$
- Log(errorMessage)
- SendPlainTextError(resp, 400, errorMessage)
- Return "error"
- End If
- ' <<< FIN NUEVA VALIDACIÓN >>>
-
- ' --- INICIO VALIDACIÓN DE PARÁMETROS (V1) ---
- If theSql.Contains("?") Or (params <> Null And params.Size > 0) Then
- Dim expectedParams As Int = theSql.Length - theSql.Replace("?", "").Length
- Dim receivedParams As Int
- If params = Null Then receivedParams = 0 Else receivedParams = params.Size
-
- If expectedParams <> receivedParams Then
- Dim errorMessage As String = $"Número de parametros equivocado para "${queryName}". Se esperaban ${expectedParams} y se recibieron ${receivedParams}."$
- Log(errorMessage)
- SendPlainTextError(resp, 400, errorMessage)
- Return "error"
- End If
- End If
- ' --- FIN VALIDACIÓN ---
-
- ' Ejecuta la consulta.
- Dim rs As ResultSet = con.ExecQuery2(theSql, params)
- If limit <= 0 Then limit = 0x7fffffff 'max int
- Dim jrs As JavaObject = rs
- Dim rsmd As JavaObject = jrs.RunMethod("getMetaData", Null)
- Dim cols As Int = rs.ColumnCount
- ' Comprime el stream de salida.
- Dim out As OutputStream = cs.WrapOutputStream(resp.OutputStream, "gzip")
- ' Escribe la cabecera de la respuesta V1.
- WriteObject(Main.VERSION, out)
- WriteObject("query", out)
- WriteInt(rs.ColumnCount, out)
- ' Escribe los nombres de las columnas.
- For i = 0 To cols - 1
- WriteObject(rs.GetColumnName(i), out)
- Next
-
- ' Itera sobre las filas del resultado.
- Do While rs.NextRow And limit > 0
- ' Escribe un byte '1' para indicar que viene una fila.
- WriteByte(1, out)
- ' Itera sobre las columnas de la fila.
- For i = 0 To cols - 1
- Dim ct As Int = rsmd.RunMethod("getColumnType", Array(i + 1))
- ' Maneja los tipos de datos binarios de forma especial.
- If ct = -2 Or ct = 2004 Or ct = -3 Or ct = -4 Then
- WriteObject(rs.GetBlob2(i), out)
- Else
- ' Escribe el valor de la columna.
- WriteObject(jrs.RunMethod("getObject", Array(i + 1)), out)
- End If
- Next
- limit = limit - 1
- Loop
- ' Escribe un byte '0' para indicar el fin de las filas.
- WriteByte(0, out)
- out.Close
- rs.Close
-
- Return "query: " & queryName
-End Sub
-
-' Escribe un único byte en el stream de salida.
-Private Sub WriteByte(value As Byte, out As OutputStream)
- out.WriteBytes(Array As Byte(value), 0, 1)
-End Sub
-
-' Serializador principal para el protocolo V1. Escribe un objeto al stream.
-Private Sub WriteObject(o As Object, out As OutputStream)
- Dim data() As Byte
- ' Escribe un byte de tipo seguido de los datos.
- If o = Null Then
- out.WriteBytes(Array As Byte(T_NULL), 0, 1)
- Else If o Is Short Then
- out.WriteBytes(Array As Byte(T_SHORT), 0, 1)
- data = bc.ShortsToBytes(Array As Short(o))
- Else If o Is Int Then
- out.WriteBytes(Array As Byte(T_INT), 0, 1)
- data = bc.IntsToBytes(Array As Int(o))
- Else If o Is Float Then
- out.WriteBytes(Array As Byte(T_FLOAT), 0, 1)
- data = bc.FloatsToBytes(Array As Float(o))
- Else If o Is Double Then
- out.WriteBytes(Array As Byte(T_DOUBLE), 0, 1)
- data = bc.DoublesToBytes(Array As Double(o))
- Else If o Is Long Then
- out.WriteBytes(Array As Byte(T_LONG), 0, 1)
- data = bc.LongsToBytes(Array As Long(o))
- Else If o Is Boolean Then
- out.WriteBytes(Array As Byte(T_BOOLEAN), 0, 1)
- Dim b As Boolean = o
- Dim data(1) As Byte
- If b Then data(0) = 1 Else data(0) = 0
- Else If GetType(o) = "[B" Then ' Si el objeto es un array de bytes (BLOB)
- data = o
- out.WriteBytes(Array As Byte(T_BLOB), 0, 1)
- ' Escribe la longitud de los datos antes de los datos mismos.
- WriteInt(data.Length, out)
- Else ' Trata todo lo demás como un String
- out.WriteBytes(Array As Byte(T_STRING), 0, 1)
- data = bc.StringToBytes(o, "UTF8")
- ' Escribe la longitud del string antes del string.
- WriteInt(data.Length, out)
- End If
- ' Escribe los bytes del dato.
- If data.Length > 0 Then out.WriteBytes(data, 0, data.Length)
-End Sub
-
-' Deserializador principal para el protocolo V1. Lee un objeto del stream.
-Private Sub ReadObject(In As InputStream) As Object
- ' Lee el primer byte para determinar el tipo de dato.
- Dim data(1) As Byte
- In.ReadBytes(data, 0, 1)
- Select data(0)
- Case T_NULL
- Return Null
- Case T_SHORT
- Dim data(2) As Byte
- Return bc.ShortsFromBytes(ReadBytesFully(In, data, data.Length))(0)
- Case T_INT
- Dim data(4) As Byte
- Return bc.IntsFromBytes(ReadBytesFully(In, data, data.Length))(0)
- Case T_LONG
- Dim data(8) As Byte
- Return bc.LongsFromBytes(ReadBytesFully(In, data, data.Length))(0)
- Case T_FLOAT
- Dim data(4) As Byte
- Return bc.FloatsFromBytes(ReadBytesFully(In, data, data.Length))(0)
- Case T_DOUBLE
- Dim data(8) As Byte
- Return bc.DoublesFromBytes(ReadBytesFully(In, data, data.Length))(0)
- Case T_BOOLEAN
- Dim b As Byte = ReadByte(In)
- Return b = 1
- Case T_BLOB
- ' Lee la longitud, luego lee esa cantidad de bytes.
- Dim len As Int = ReadInt(In)
- Dim data(len) As Byte
- Return ReadBytesFully(In, data, data.Length)
- Case Else ' T_STRING
- ' Lee la longitud, luego lee esa cantidad de bytes y los convierte a string.
- Dim len As Int = ReadInt(In)
- Dim data(len) As Byte
- ReadBytesFully(In, data, data.Length)
- Return BytesToString(data, 0, data.Length, "UTF8")
- End Select
-End Sub
-
-' Se asegura de leer exactamente la cantidad de bytes solicitada del stream.
-Private Sub ReadBytesFully(In As InputStream, Data() As Byte, Len As Int) As Byte()
- Dim count = 0, Read As Int
- ' Sigue leyendo en un bucle hasta llenar el buffer, por si los datos llegan en partes.
- Do While count < Len And Read > -1
- Read = In.ReadBytes(Data, count, Len - count)
- count = count + Read
- Loop
- Return Data
-End Sub
-
-' Escribe un entero (4 bytes) en el stream.
-Private Sub WriteInt(i As Int, out As OutputStream)
- Dim data() As Byte
- data = bc.IntsToBytes(Array As Int(i))
- out.WriteBytes(data, 0, data.Length)
-End Sub
-
-' Lee un entero (4 bytes) del stream.
-Private Sub ReadInt(In As InputStream) As Int
- Dim data(4) As Byte
- Return bc.IntsFromBytes(ReadBytesFully(In, data, data.Length))(0)
-End Sub
-
-' Lee un solo byte del stream.
-Private Sub ReadByte(In As InputStream) As Byte
- Dim data(1) As Byte
- In.ReadBytes(data, 0, 1)
- Return data(0)
-End Sub
-
-' Lee una lista de objetos del stream (protocolo V1).
-Private Sub ReadList(in As InputStream) As List
- ' Primero lee la cantidad de elementos en la lista.
- Dim len As Int = ReadInt(in)
- Dim l1 As List
- l1.Initialize
- ' Luego lee cada objeto uno por uno y lo añade a la lista.
- For i = 0 To len - 1
- l1.Add(ReadObject(in))
- Next
- Return l1
-End Sub
-'#end If
-
-' Envía una respuesta de error en formato de texto plano.
-' Esto evita la página de error HTML por defecto que genera resp.SendError.
-' resp: El objeto ServletResponse para enviar la respuesta.
-' statusCode: El código de estado HTTP (ej. 400 para Bad Request, 500 para Internal Server Error).
-' errorMessage: El mensaje de error que se enviará al cliente.
-Private Sub SendPlainTextError(resp As ServletResponse, statusCode As Int, errorMessage As String)
- Try
- ' Establece el código de estado HTTP (ej. 400, 500).
- resp.Status = statusCode
-
- ' Define el tipo de contenido como texto plano, con codificación UTF-8 para soportar acentos.
- resp.ContentType = "text/plain; charset=utf-8"
-
- ' Obtiene el OutputStream de la respuesta para escribir los datos directamente.
- Dim out As OutputStream = resp.OutputStream
-
- ' Convierte el mensaje de error a un array de bytes usando UTF-8.
- Dim data() As Byte = errorMessage.GetBytes("UTF8")
-
- ' Escribe los bytes en el stream de salida.
- out.WriteBytes(data, 0, data.Length)
-
- ' Cierra el stream para asegurar que todos los datos se envíen correctamente.
- out.Close
- Catch
- ' Si algo falla al intentar enviar la respuesta de error, lo registra en el log
- ' para que no se pierda la causa original del problema.
- Log("Error sending plain text error response: " & LastException)
- End Try
-End Sub
diff --git a/Manager.bas b/Manager.bas
index f644ed2..c9d456a 100644
--- a/Manager.bas
+++ b/Manager.bas
@@ -113,11 +113,17 @@ Sub Handle(req As ServletRequest, resp As ServletResponse)
Else If Command = "rsx" Then
Log($"Ejecutamos ${File.DirApp}\start.bat"$)
sb.Append($"Ejecutamos ${File.DirApp}\start.bat"$)
- ' Public shl As Shell...
+ Public shl As Shell
+ shl.Initialize("shl","cmd",Array("/c",File.DirApp & "\start.bat " & Main.srvr.Port))
+ shl.WorkingDirectory = File.DirApp
+ shl.Run(-1)
Else If Command = "rpm2" Then
Log($"Ejecutamos ${File.DirApp}\reiniciaProcesoPM2.bat"$)
sb.Append($"Ejecutamos ${File.DirApp}\reiniciaProcesoPM2.bat"$)
- ' Public shl As Shell...
+ Public shl As Shell
+ shl.Initialize("shl","cmd",Array("/c",File.DirApp & "\reiniciaProcesoPM2.bat " & Main.srvr.Port))
+ shl.WorkingDirectory = File.DirApp
+ shl.Run(-1)
Else If Command = "reviveBow" Then
Log($"Ejecutamos ${File.DirApp}\reiniciaProcesoBow.bat"$)
sb.Append($"Ejecutamos ${File.DirApp}\reiniciaProcesoBow.bat
"$)
diff --git a/README0.md b/README0.md
deleted file mode 100644
index 7fd3b3c..0000000
--- a/README0.md
+++ /dev/null
@@ -1,48 +0,0 @@
-# jRDC-Multi (B4J)
-Servidor de DBRequest que puede cargar hasta 4 archivos de config.properties al mismo tiempo.
-
-Los archivos se deben de llamar:
-
- - config.propierties
- - config.DB2.properties
- - config.DB3.properties
- - config.DB4.properties
-
-No es necesario que sean 4 archivos, solo toma en cuenta los archivos existentes en el directorio.
-
-En la aplicacion movil, al URL del servidor se le agrega al final /DB2, /DB3 o /DB4. (Puerto de ejemplo: 1781)
-
-- Para usar el config.properties => http://keymon.lat:1781
-- Para usar el config.DB2.properties => http://keymon.lat:1781/DB2
-- Para usar el config.DB3.properties => http://keymon.lat:1781/DB3
-- Para usar el config.DB4.properties => http://keymon.lat:1781/DB4
-
-El puerto es el mismo para todos los archivos, **sin importar** que diga en cada archivo, solo toma el puerto especificado en el **primer** config.properties.
-
-El usuario, contraseña y JdbcUrl, **si** los toma del archivo correspondiente.
-
-Se puede revisar el **estatus** del servidor en el URL:
-
-- http://keymon.lat:1781/test
-
-Se puede forzar al servidor (**sin reiniciarlo**) a que **recargue** los archivos config.properties en el URL:
-
-- http://keymon.lat:1781/manager?command=reload
-
-Se puede reiniciar el servidor con el URL:
-
-- http://keymon.lat:1781/manager?command=rsx
-- Este comando utiliza los archivos start.bat, start2.bat y stop.bat
-
-Si se esta corriendo el servidor con PM2, se puede reinciar con el URL:
-
-- http://keymon.lat:1781/manager?command=rpm2
-- Este comando ejecuta el archivo reiniciaProcesoPM2.bat, y **asume** que el nombre del proceso es "RDC-Multi", si no es asi, hay que **modificar** el archivo .bat
-
-## Agregar drivers de mas bases de datos
-
- Si se necesitan agregar mas controladores para conectarse a otras bases de datos, hay que agregar una linea a "Main":
-
- - #AdditionalJar: ojdbc11 <= este es el nombre del archivo .jar, en este caso "C:\Android\AdditionalLibs\B4J\ojdbc11.jar"
-
- - Al compilar la aplicación, el archivo del controlador se incluye en el archivo .jar del servidor (jRDC-Multi.jar) y no es necesario copiarlo o agregarlo al directorio del servidor en producción.
diff --git a/Readme1.md b/Readme1.md
deleted file mode 100644
index 85c3cdd..0000000
--- a/Readme1.md
+++ /dev/null
@@ -1,105 +0,0 @@
-# Servidor jRDC2-Multi Modificado (B4J)
-
-## 1. Introducción
-
-Este proyecto es una versión modificada del servidor [jRDC2 original](https://www.b4x.com/android/forum/threads/b4x-jrdc2-b4j-implementation-of-rdc-remote-database-connector.61801/#content), diseñada para actuar como un backend robusto y flexible. Su función principal es recibir peticiones HTTP, ejecutar comandos SQL predefinidos contra una base de datos y devolver los resultados en un formato estructurado.
-
-Ha sido adaptado para servir tanto a clientes nativos (B4A/B4i) como a clientes web modernos (JavaScript, a través de frameworks como React, Vue, Angular, etc.).
-
-## 2. Características Principales
-
-* **Soporte para Múltiples Bases de Datos**: Puede cargar y gestionar hasta 4 archivos de configuración (`config.properties`) simultáneamente.
-* **Comandos SQL Externalizados**: Las sentencias SQL se definen en los archivos de configuración, permitiendo modificarlas sin recompilar el servidor.
-* **Doble Handler de Peticiones**: Incluye un handler clásico para clientes B4X y un handler JSON para clientes web.
-* **Validaciones de Seguridad**: Verifica la existencia de comandos y la correspondencia en el número de parámetros.
-* **Administración Remota**: Permite verificar el estado, recargar la configuración y reiniciar el servidor a través de URLs específicas.
-
-## 3. Configuración
-
-### 3.1. Archivos de Configuración
-
-El sistema está preparado para manejar hasta **cuatro configuraciones de bases de datos** (de `DB1` a `DB4`). No es necesario tener los cuatro archivos; el servidor cargará únicamente los que encuentre.
-
-La nomenclatura de los archivos es fundamental:
-
-* `config.properties` (para `DB1`)
-* `config.DB2.properties`
-* `config.DB3.properties`
-* `config.DB4.properties`
-
-**Notas importantes:**
-
-* El **puerto** del servidor se toma **únicamente** del archivo principal `config.properties`, sin importar lo que digan los demás.
-* Los datos de conexión (`JdbcUrl`, usuario, contraseña) sí se toman del archivo correspondiente a cada base de datos.
-
-### 3.2. Añadir Drivers de Bases de Datos Adicionales
-
-Si necesitas conectarte a otros tipos de bases de datos (ej. Oracle), debes agregar el archivo del controlador `.jar` al proyecto antes de compilar. En el módulo `Main`, añade una línea como la siguiente:
-
-```b4x
-' Este es el nombre del archivo .jar, en este caso "C:\Ruta\Adicional\ojdbc11.jar"
-#AdditionalJar: ojdbc11
-````
-
-Al compilar, el driver se incluirá en el `.jar` final del servidor, por lo que no será necesario copiarlo por separado al directorio de producción.
-
-## 4\. Uso del Handler Clásico (Para Clientes B4X)
-
-Este handler mantiene la compatibilidad con `DBRequestManager`. La selección de la base de datos se realiza dinámicamente a través de la URL.
-
- * Para `config.properties` =\> `http://tu-dominio.com:8090`
- * Para `config.DB2.properties` =\> `http://tu-dominio.com:8090/DB2`
- * Para `config.DB3.properties` =\> `http://tu-dominio.com:8090/DB3`
- * Para `config.DB4.properties` =\> `http://tu-dominio.com:8090/DB4`
-
-## 5\. Uso del `DB1JsonHandler` (Para Clientes Web)
-
-Este handler es para clientes que se comunican vía JSON.
-
-### 5.1. Endpoint y Métodos
-
-Las peticiones van al endpoint `/DBJ` y deben incluir un parámetro `j` con el JSON. Soporta `GET` y `POST`.
-
-**Ejemplo con `GET`:**
-`http://tu-dominio.com:8090/db1json?j={"dbx":"DB2","query":"get_user","exec":"executeQuery","params":{"par1":"CDAZA"}}`
-
-### 5.2. Formato del Parámetro `j`
-
-```json
-{
- "exec": "executeQuery",
- "query": "nombre_del_comando_sql",
- "dbx": "DB1",
- "params": {
- "par1": "valor1",
- "par2": 123
- }
-}
-```
-
- * `exec`: `"executeQuery"` (para `SELECT`) o `"executeCommand"` (para `INSERT`, `UPDATE`, etc.).
- * `query`: Nombre del comando SQL en el archivo de configuración.
- * `dbx` (opcional): La llave de la BD (`DB1`, `DB2`, etc.). Si se omite, usa `DB1`.
- * `params` (opcional): Objeto con los parámetros.
-
-### 5.3. ¡Importante\! Envío de Parámetros
-
-El servidor ordena las claves de los parámetros alfabéticamente. Para asegurar el orden correcto, **nombra las claves secuencialmente**: `"par1"`, `"par2"`, etc.
-
-> **Nota para más de 9 parámetros**: Usa un cero inicial para mantener el orden (`"par01"`, `"par02"`, ..., `"par10"`).
-
-### 5.4. Respuestas JSON
-
-Las respuestas siempre incluyen `"success": true` o `"success": false`, con los datos en `"result"` o el error en `"error"`.
-
-## 6\. Administración del Servidor
-
-Se pueden ejecutar comandos de gestión directamente desde un navegador.
-
- * **Verificar Estado**: `http://tu-dominio.com:8090/test`
- * **Recargar Configuración**: `http://tu-dominio.com:8090/manager?command=reload`
- (Vuelve a leer todos los archivos `config.*.properties` sin reiniciar el servidor).
- * **Reiniciar Servidor (Estándar)**: `http://tu-dominio.com:8090/manager?command=rsx`
- (Ejecuta los scripts `start.bat`, `start2.bat` y `stop.bat`).
- * **Reiniciar Servidor (con PM2)**: `http://tu-dominio.com:8090/manager?command=rpm2`
- (Ejecuta `reiniciaProcesoPM2.bat` y asume que el nombre del proceso es "RDC-Multi". Modificar el `.bat` si el nombre es diferente).
diff --git a/jRDC_Multi.b4j b/jRDC_Multi.b4j
index 432e1b0..d4ee163 100644
--- a/jRDC_Multi.b4j
+++ b/jRDC_Multi.b4j
@@ -30,31 +30,27 @@ Library6=jshell
Library7=json
Library8=jsql
Library9=bcrypt
-Module1=B4AHandler
-Module10=LoginHandler
-Module11=LogoutHandler
-Module12=Manager
-Module13=ping
-Module14=RDCConnector
-Module15=TestHandler
-Module2=ChangePassHandler
-Module3=DB2Handler
-Module4=DB3Handler
-Module5=DB4Handler
-Module6=DBHandlerJSON
-Module7=DoLoginHandler
-Module8=GlobalParameters
-Module9=HandlerB4X
+Module1=ChangePassHandler
+Module10=RDCConnector
+Module11=TestHandler
+Module2=DBHandlerB4X
+Module3=DBHandlerJSON
+Module4=DoLoginHandler
+Module5=GlobalParameters
+Module6=LoginHandler
+Module7=LogoutHandler
+Module8=Manager
+Module9=ping
NumberOfFiles=10
NumberOfLibraries=9
-NumberOfModules=15
+NumberOfModules=11
Version=10.3
@EndOfDesignText@
'Non-UI application (console / server application)
#Region Project Attributes
#CommandLineArgs:
#MergeLibraries: True
- ' VERSION 5.08.30
+ ' VERSION 5.09.01
'###########################################################################################################
'###################### PULL #############################################################
'Ctrl + click ide://run?file=%WINDIR%\System32\cmd.exe&Args=/c&Args=git&Args=pull
@@ -140,7 +136,7 @@ Sub AppStart (Args() As String)
srvr.AddHandler("/dbrquery", "DBHandlerJSON", False)
' srvr.AddHandler("/*", "DB1Handler", False) ' Si no se especifica una base de datos, entonces asignamos la solicitud a la DB1.
- srvr.AddHandler("/*", "HandlerB4X", False)
+ srvr.AddHandler("/*", "DBHandlerB4X", False)
srvr.Start
Log("===========================================================")
diff --git a/jRDC_Multi.b4j.meta b/jRDC_Multi.b4j.meta
index f59b034..678255c 100644
--- a/jRDC_Multi.b4j.meta
+++ b/jRDC_Multi.b4j.meta
@@ -2,10 +2,6 @@
ModuleBookmarks1=
ModuleBookmarks10=
ModuleBookmarks11=
-ModuleBookmarks12=
-ModuleBookmarks13=
-ModuleBookmarks14=
-ModuleBookmarks15=
ModuleBookmarks2=
ModuleBookmarks3=
ModuleBookmarks4=
@@ -18,10 +14,6 @@ ModuleBreakpoints0=
ModuleBreakpoints1=
ModuleBreakpoints10=
ModuleBreakpoints11=
-ModuleBreakpoints12=
-ModuleBreakpoints13=
-ModuleBreakpoints14=
-ModuleBreakpoints15=
ModuleBreakpoints2=
ModuleBreakpoints3=
ModuleBreakpoints4=
@@ -34,18 +26,14 @@ ModuleClosedNodes0=
ModuleClosedNodes1=
ModuleClosedNodes10=
ModuleClosedNodes11=
-ModuleClosedNodes12=
-ModuleClosedNodes13=
-ModuleClosedNodes14=
-ModuleClosedNodes15=
ModuleClosedNodes2=
-ModuleClosedNodes3=4,5,6
+ModuleClosedNodes3=
ModuleClosedNodes4=
ModuleClosedNodes5=
ModuleClosedNodes6=
ModuleClosedNodes7=
ModuleClosedNodes8=
ModuleClosedNodes9=
-NavigationStack=DoLoginHandler,Class_Globals,2,0,DoLoginHandler,Initialize,7,0,Manager,Initialize,98,0,ChangePassHandler,Handle,27,0,DoLoginHandler,Handle,9,0,Manager,Handle0,202,6,Main,Process_Globals,24,0,Main,AppStart,97,0,TestHandler,Handle,11,0,Manager,Handle,46,6,LoginHandler,Handle,16,1
+NavigationStack=DoLoginHandler,Initialize,7,0,Manager,Initialize,98,0,ChangePassHandler,Handle,27,0,DoLoginHandler,Handle,9,0,Manager,Handle0,202,6,TestHandler,Handle,11,0,LoginHandler,Handle,16,1,Manager,Handle,110,6,Main,Process_Globals,24,0,Main,AppStart,85,0
SelectedBuild=0
-VisibleModules=9,6,14,12,15
+VisibleModules=2,3,10,8,11,6
diff --git a/reiniciaProcesoPM2.bat b/reiniciaProcesoPM2.bat
index dc1bd70..d9912fe 100644
--- a/reiniciaProcesoPM2.bat
+++ b/reiniciaProcesoPM2.bat
@@ -4,6 +4,6 @@
set "params=%*"
cd /d "%~dp0" && ( if exist "%temp%\getadmin.vbs" del "%temp%\getadmin.vbs" ) && fsutil dirty query %systemdrive% 1>nul 2>nul || ( echo Set UAC = CreateObject^("Shell.Application"^) : UAC.ShellExecute "cmd.exe", "/k cd ""%~sdp0"" && ""%~s0"" %params%", "", "runas", 1 >> "%temp%\getadmin.vbs" && "%temp%\getadmin.vbs" && exit /B )
-pm2 start RDC-Multi
+pm2 restart jRDC-Multi
exit
\ No newline at end of file