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