diff --git a/.github/workflows/Build.yml b/.github/workflows/Build.yml
index edfb8415f..f822ba417 100644
--- a/.github/workflows/Build.yml
+++ b/.github/workflows/Build.yml
@@ -190,14 +190,14 @@ jobs:
$PushToAzureArgs += "--skip-duplicate"
}
- dotnet $PushToAzureArgs
+ dotnet $PushToAzureArgs || exit 1
$pushedToAzure += 1
if (!$IsPrerelease) {
- dotnet $PushToGitHubArgs
+ dotnet $PushToGitHubArgs || exit 1
$pushedToGitHub += 1
- dotnet $PushToNugetArgs
+ dotnet $PushToNugetArgs || exit 1
$pushedToNuget += 1
}
diff --git a/README.md b/README.md
index 04de49d5b..6f4534743 100644
--- a/README.md
+++ b/README.md
@@ -1,11 +1,11 @@
# GeneXus Standard Classes for .NET and .NET Framework
GeneXus Standard Classes for .NET and .NET Framework generators.
-## Build status
-| Branch | Status
-|---|---
-|master|[](https://github.com/genexuslabs/dotnetclasses/actions?query=workflow%3ABuild+branch%3Amaster)
-|beta|[](https://github.com/genexuslabs/dotnetclasses/actions?query=workflow%3ABuild+branch%3Abeta)
+## Repo status
+| Branch | Build | Security
+|---|---|---
+|master|[](https://github.com/genexuslabs/dotnetclasses/actions?query=workflow%3ABuild+branch%3Amaster)|[](https://github.com/genexuslabs/DotNetClasses/actions/workflows/codeql-analysis.yml)
+|beta|[](https://github.com/genexuslabs/dotnetclasses/actions?query=workflow%3ABuild+branch%3Abeta)|[](https://github.com/genexuslabs/DotNetClasses/actions/workflows/codeql-analysis.yml)
## Modules
diff --git a/dotnet/Directory.Build.props b/dotnet/Directory.Build.props
index 03e14d90e..1be77468d 100644
--- a/dotnet/Directory.Build.props
+++ b/dotnet/Directory.Build.props
@@ -3,7 +3,7 @@
11.0.0.0
1
$([MSBuild]::Add($(MajorFileVersion), 100))
- 27
+ 30
$(COMMIT_NUMBER)
0
$(MajorFileVersion).$(MinorFileVersion).$(PatchFileVersion)
@@ -29,6 +29,8 @@
NU5105;CS0618;CS8032;CS0618;SYSLIB0021;SYSLIB0023
true
True
+
+ NU1900;NU1901;NU1902;NU1903;NU1904
diff --git a/dotnet/DotNetStandardClasses.sln b/dotnet/DotNetStandardClasses.sln
index 16834f162..01e391f0b 100644
--- a/dotnet/DotNetStandardClasses.sln
+++ b/dotnet/DotNetStandardClasses.sln
@@ -257,6 +257,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GXOtel.Diagnostics", "src\d
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetCoreCmdTest", "test\DotNetCoreCmdTest\DotNetCoreCmdTest.csproj", "{956402BD-AC8C-426E-961B-B77B3F3EDAEB}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "benchmarks", "benchmarks", "{46DAAFD1-FAF5-4904-8EC5-406BE04E5538}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LogTest", "test\benchmarks\LogTest\LogTest.csproj", "{A1DBDCE0-4F09-445F-A202-9B260CDD46CF}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AccessTokenController_Test", "test\NativeAccessControllerTest\AccessTokenController_Test.csproj", "{A5589382-DB6F-4450-AE2B-6C6AA1643EF1}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -627,7 +633,14 @@ Global
{956402BD-AC8C-426E-961B-B77B3F3EDAEB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{956402BD-AC8C-426E-961B-B77B3F3EDAEB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{956402BD-AC8C-426E-961B-B77B3F3EDAEB}.Release|Any CPU.Build.0 = Release|Any CPU
-
+ {A1DBDCE0-4F09-445F-A202-9B260CDD46CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A1DBDCE0-4F09-445F-A202-9B260CDD46CF}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A1DBDCE0-4F09-445F-A202-9B260CDD46CF}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A1DBDCE0-4F09-445F-A202-9B260CDD46CF}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A5589382-DB6F-4450-AE2B-6C6AA1643EF1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A5589382-DB6F-4450-AE2B-6C6AA1643EF1}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A5589382-DB6F-4450-AE2B-6C6AA1643EF1}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A5589382-DB6F-4450-AE2B-6C6AA1643EF1}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -751,7 +764,9 @@ Global
{0FCFB078-5584-469F-92CC-61B0A6216D0D} = {1D6F1776-FF4B-46C2-9B3D-BC46CCF049DC}
{A42066E8-DDB9-4767-AEFA-E6D13EFF051A} = {BBE020D4-C0FF-41A9-9EB1-D1EE12CC4BB8}
{956402BD-AC8C-426E-961B-B77B3F3EDAEB} = {1D6F1776-FF4B-46C2-9B3D-BC46CCF049DC}
-
+ {46DAAFD1-FAF5-4904-8EC5-406BE04E5538} = {1D6F1776-FF4B-46C2-9B3D-BC46CCF049DC}
+ {A1DBDCE0-4F09-445F-A202-9B260CDD46CF} = {46DAAFD1-FAF5-4904-8EC5-406BE04E5538}
+ {A5589382-DB6F-4450-AE2B-6C6AA1643EF1} = {1D6F1776-FF4B-46C2-9B3D-BC46CCF049DC}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {E18684C9-7D76-45CD-BF24-E3944B7F174C}
diff --git a/dotnet/src/dotnetcommon/DynService.Core/DynService.Core.csproj b/dotnet/src/dotnetcommon/DynService.Core/DynService.Core.csproj
index dde00ac2a..45d27f4a7 100644
--- a/dotnet/src/dotnetcommon/DynService.Core/DynService.Core.csproj
+++ b/dotnet/src/dotnetcommon/DynService.Core/DynService.Core.csproj
@@ -18,4 +18,8 @@
+
+
+
+
\ No newline at end of file
diff --git a/dotnet/src/dotnetcommon/DynService.Dynamo/DynService.DynamoDB.csproj b/dotnet/src/dotnetcommon/DynService.Dynamo/DynService.DynamoDB.csproj
index 58d869618..16e8434b3 100644
--- a/dotnet/src/dotnetcommon/DynService.Dynamo/DynService.DynamoDB.csproj
+++ b/dotnet/src/dotnetcommon/DynService.Dynamo/DynService.DynamoDB.csproj
@@ -13,6 +13,7 @@
+
diff --git a/dotnet/src/dotnetcommon/GxEncrypt/GxEncrypt.csproj b/dotnet/src/dotnetcommon/GxEncrypt/GxEncrypt.csproj
index 5f7d6885f..f4518c1e4 100644
--- a/dotnet/src/dotnetcommon/GxEncrypt/GxEncrypt.csproj
+++ b/dotnet/src/dotnetcommon/GxEncrypt/GxEncrypt.csproj
@@ -6,11 +6,7 @@
Encrypt64 Decrypt64
GeneXus.Encrypt
-
-
+
+
-
-
-
-
\ No newline at end of file
diff --git a/dotnet/src/dotnetcore/DynService/Cosmos/DynService.CosmosDB.csproj b/dotnet/src/dotnetcore/DynService/Cosmos/DynService.CosmosDB.csproj
index 281cac6cd..0c61c7593 100644
--- a/dotnet/src/dotnetcore/DynService/Cosmos/DynService.CosmosDB.csproj
+++ b/dotnet/src/dotnetcore/DynService/Cosmos/DynService.CosmosDB.csproj
@@ -13,8 +13,9 @@
-
+
+
diff --git a/dotnet/src/dotnetcore/DynService/OData/DynServiceOData.csproj b/dotnet/src/dotnetcore/DynService/OData/DynServiceOData.csproj
index 0d2a21dba..d57e3d3ee 100644
--- a/dotnet/src/dotnetcore/DynService/OData/DynServiceOData.csproj
+++ b/dotnet/src/dotnetcore/DynService/OData/DynServiceOData.csproj
@@ -12,6 +12,7 @@
+
diff --git a/dotnet/src/dotnetcore/GxClasses.Web/GxClasses.Web.csproj b/dotnet/src/dotnetcore/GxClasses.Web/GxClasses.Web.csproj
index b838905a3..e95fc82c8 100644
--- a/dotnet/src/dotnetcore/GxClasses.Web/GxClasses.Web.csproj
+++ b/dotnet/src/dotnetcore/GxClasses.Web/GxClasses.Web.csproj
@@ -29,8 +29,9 @@
-
-
+
+
+
@@ -44,15 +45,5 @@
-
-
- False
- ..\libs\net8.0\Jayrock.dll
-
-
- False
- ..\libs\Jayrock.dll
-
-
\ No newline at end of file
diff --git a/dotnet/src/dotnetcore/GxClasses.Web/Middleware/GXRouting.cs b/dotnet/src/dotnetcore/GxClasses.Web/Middleware/GXRouting.cs
index 8ffe0c2de..bd3ae4a37 100644
--- a/dotnet/src/dotnetcore/GxClasses.Web/Middleware/GXRouting.cs
+++ b/dotnet/src/dotnetcore/GxClasses.Web/Middleware/GXRouting.cs
@@ -58,6 +58,7 @@ public GXRouting(string baseURL)
ServicesGroupSetting();
ServicesFunctionsMetadata();
GetAzureDeploy();
+ SvcFiles();
}
static public List GetRouteController(Dictionary apiPaths,
@@ -377,8 +378,7 @@ public GxRestWrapper GetController(HttpContext context, ControllerInfo controlle
GxContext gxContext = GxContext.CreateDefaultInstance();
gxContext.HttpContext = context;
context.NewSessionCheck();
- string nspace;
- Config.GetValueOf("AppMainNamespace", out nspace);
+ string nspace = Preferences.AppMainNamespace;
String tmpController = controller;
String addNspace = string.Empty;
@@ -397,7 +397,7 @@ public GxRestWrapper GetController(HttpContext context, ControllerInfo controlle
bool privateDirExists = Directory.Exists(privateDir);
GXLogging.Debug(log, $"PrivateDir:{privateDir} asssemblycontroller:{asssemblycontroller}");
-
+ string svcFile=null;
if (privateDirExists && File.Exists(Path.Combine(privateDir, $"{asssemblycontroller.ToLower()}.grp.json")))
{
controller = tmpController;
@@ -412,9 +412,7 @@ public GxRestWrapper GetController(HttpContext context, ControllerInfo controlle
else
{
string controllerLower = controller.ToLower();
- string svcFile = SvcFile($"{controller}{SvcExtension}");
- if (svcFile==null)
- svcFile = SvcFile($"{controllerLower}{SvcExtension}");
+ svcFile = SvcFile($"{controller}{SvcExtension}");
if (File.Exists(svcFile))
{
string[] controllerAssemblyQualifiedName = new string(File.ReadLines(svcFile).First().SkipWhile(c => c != '"')
@@ -446,22 +444,26 @@ public GxRestWrapper GetController(HttpContext context, ControllerInfo controlle
GXLogging.Warn(log, $"Controller was not found");
return null;
}
- string SvcFile(string controller)
+
+ private void SvcFiles()
{
- if (svcFiles == null)
+ svcFiles = new HashSet(new CaseInsensitiveStringEqualityComparer());
+ foreach (string file in Directory.GetFiles(ContentRootPath, SvcExtensionPattern, SearchOption.AllDirectories))
{
- svcFiles = new HashSet();
- foreach (string file in Directory.GetFiles(ContentRootPath, SvcExtensionPattern, SearchOption.AllDirectories))
- {
- svcFiles.Add(file);
- }
+ svcFiles.Add(file);
}
+ }
+ string SvcFile(string controller)
+ {
string fileName;
- string controllerFullName = Path.Combine(ContentRootPath, controller);
- if (svcFiles.TryGetValue(new FileInfo(controllerFullName).FullName, out fileName))
+ string controllerFullName = new FileInfo(Path.Combine(ContentRootPath, controller)).FullName;
+ if (svcFiles.TryGetValue(controllerFullName, out fileName))
return fileName;
else
+ {
+ GXLogging.Warn(log, "Service file not found:" + controllerFullName);
return null;
+ }
}
public void ServicesGroupSetting()
@@ -601,7 +603,18 @@ public string GAM
}
+ internal class CaseInsensitiveStringEqualityComparer : IEqualityComparer
+ {
+ public bool Equals(string x, string y)
+ {
+ return string.Equals(x, y, StringComparison.OrdinalIgnoreCase);
+ }
+ public int GetHashCode(string obj)
+ {
+ return obj.ToLower().GetHashCode();
+ }
+ }
public static class AzureFeature
{
public const string AzureServerless = "AzureServerless";
diff --git a/dotnet/src/dotnetcore/GxClasses.Web/Middleware/HandlerFactory.cs b/dotnet/src/dotnetcore/GxClasses.Web/Middleware/HandlerFactory.cs
index d9eb0b14a..d072c5d27 100644
--- a/dotnet/src/dotnetcore/GxClasses.Web/Middleware/HandlerFactory.cs
+++ b/dotnet/src/dotnetcore/GxClasses.Web/Middleware/HandlerFactory.cs
@@ -8,6 +8,7 @@
using GeneXus.Configuration;
using GeneXus.Http;
using GeneXus.Mime;
+using GeneXus.Procedure;
using GeneXus.Utils;
using Microsoft.AspNetCore.Http;
@@ -37,25 +38,6 @@ public class HandlerFactory
{"gxoauthuserinfo",typeof(GXOAuthUserInfo)},
{"gxoauthaccesstoken",typeof(GXOAuthAccessToken)},
{"gxmulticall",typeof(GXMultiCall)}};
- static Dictionary _aspxRewrite = new Dictionary(){
- {"oauth/access_token","gxoauthaccesstoken.aspx"},
- {"oauth/logout","gxoauthlogout.aspx"},
- {"oauth/userinfo","gxoauthuserinfo.aspx"},
- {"oauth/gam/signin","agamextauthinput.aspx"},
- {"oauth/gam/callback","agamextauthinput.aspx"},
- {"oauth/gam/access_token","agamoauth20getaccesstoken.aspx"},
- {"oauth/gam/userinfo","agamoauth20getuserinfo.aspx"},
- {"oauth/gam/signout","agamextauthinput.aspx"},
- {"saml/gam/signin","Saml2/SignIn"},
- {"saml/gam/callback","gamexternalauthenticationinputsaml20_ws.aspx"},
- {"saml/gam/signout","Saml2/Logout"},
- {"oauth/requesttokenservice","agamstsauthappgetaccesstoken.aspx"},
- {"oauth/queryaccesstoken","agamstsauthappvalidaccesstoken.aspx"},
- {"oauth/gam/v2.0/access_token","agamoauth20getaccesstoken_v20.aspx"},
- {"oauth/gam/v2.0/userinfo","agamoauth20getuserinfo_v20.aspx"},
- {"oauth/gam/v2.0/requesttokenanduserinfo","aGAMSSORestRequestTokenAndUserInfo_v20.aspx"}};
- private const string QUERYVIEWER_NAMESPACE = "QueryViewer.Services";
- private const string GXFLOW_NSPACE = "GXflow.Programs";
private static List GxNamespaces;
public HandlerFactory()
@@ -93,8 +75,16 @@ public async Task Invoke(HttpContext context)
handler.sendAdditionalHeaders();
return Task.CompletedTask;
});
- handler.ProcessRequest(context);
- await Task.CompletedTask;
+ GXWebProcedure gxWebProc = handler as GXWebProcedure;
+ if (gxWebProc != null && gxWebProc.GetAsyncEnabledInternal())
+ {
+ await gxWebProc.ProcessRequestAsync(context);
+ }
+ else
+ {
+ handler.ProcessRequest(context);
+ await Task.CompletedTask;
+ }
handler.ControlOutputWriter?.Flush();
}
else
@@ -122,9 +112,9 @@ private static string ObjectUrl(string requestPath, string basePath)
}
lastSegment = CleanUploadUrlSuffix(lastSegment.TrimStart('/').ToLower());
GXLogging.Debug(log, "ObjectUrl:", lastSegment);
- if (_aspxRewrite.ContainsKey(lastSegment))
+ if (HttpHelper.GAMServices.ContainsKey(lastSegment))
{
- return _aspxRewrite[lastSegment];
+ return HttpHelper.GAMServices[lastSegment];
}
return lastSegment;
}
@@ -167,11 +157,15 @@ public IHttpHandler GetHandler(HttpContext context, string requestType, string u
string className;
if (cname.StartsWith("agxpl_", StringComparison.OrdinalIgnoreCase) || cname.Equals("gxqueryviewerforsd", StringComparison.OrdinalIgnoreCase))
{
- className = $"{QUERYVIEWER_NAMESPACE}.{cname}";
+ className = $"{HttpHelper.QUERYVIEWER_NAMESPACE}.{cname}";
}
else if (Preferences.GxpmEnabled && (cname.StartsWith("awf", StringComparison.OrdinalIgnoreCase) || cname.StartsWith("wf", StringComparison.OrdinalIgnoreCase) || cname.StartsWith("apwf", StringComparison.OrdinalIgnoreCase)))
{
- className = $"{GXFLOW_NSPACE}.{cname}";
+ className = $"{HttpHelper.GXFLOW_NSPACE}.{cname}";
+ }
+ else if (HttpHelper.GamServicesInternalName.Contains(cname))
+ {
+ className = $"{HttpHelper.GAM_NSPACE}.{cname}";
}
else
{
diff --git a/dotnet/src/dotnetcore/GxClasses.Web/Notifications/WebSocket/WSHandler.cs b/dotnet/src/dotnetcore/GxClasses.Web/Notifications/WebSocket/WSHandler.cs
index eea674266..84430c30a 100644
--- a/dotnet/src/dotnetcore/GxClasses.Web/Notifications/WebSocket/WSHandler.cs
+++ b/dotnet/src/dotnetcore/GxClasses.Web/Notifications/WebSocket/WSHandler.cs
@@ -1,3 +1,8 @@
+#if NETCORE
+using GeneXus.Application;
+#else
+using Jayrock.Json;
+#endif
using System;
using System.Collections.Generic;
using System.Linq;
@@ -11,7 +16,6 @@
using GeneXus.Procedure;
using GeneXus.Services;
using GeneXus.Utils;
-using Jayrock.Json;
namespace GeneXus.Http.WebSocket
{
@@ -119,8 +123,7 @@ private bool ExecuteHandler(HandlerType type, Object[] parameters)
{
try
{
- string nSpace = string.Empty;
- Config.GetValueOf("AppMainNamespace", out nSpace);
+ string nSpace = Preferences.AppMainNamespace;
GXProcedure obj = null;
try
{
diff --git a/dotnet/src/dotnetcore/GxClasses/Domain/JArray.cs b/dotnet/src/dotnetcore/GxClasses/Domain/JArray.cs
new file mode 100644
index 000000000..8d53a77e4
--- /dev/null
+++ b/dotnet/src/dotnetcore/GxClasses/Domain/JArray.cs
@@ -0,0 +1,360 @@
+#region License, Terms and Conditions
+//
+// Jayrock - A JSON-RPC implementation for the Microsoft .NET Framework
+// Written by Atif Aziz (atif.aziz@skybow.com)
+// Copyright (c) Atif Aziz. All rights reserved.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License as published by the Free
+// Software Foundation; either version 2.1 of the License, or (at your option)
+// any later version.
+//
+// This library is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+// FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+// details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this library; if not, write to the Free Software Foundation, Inc.,
+// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+#endregion
+
+namespace GeneXus.Application
+{
+ #region Imports
+
+ using System;
+ using System.Collections;
+ using System.Globalization;
+ using System.Text;
+ using GeneXus.Utils;
+
+ #endregion
+
+ ///
+ /// An ordered sequence of values. This class also provides a number of
+ /// methods that can be found on a JavaScript Array for sake of parity.
+ ///
+ ///
+ ///
+ /// Public Domain 2002 JSON.org, ported to C# by Are Bjolseth (teleplan.no)
+ /// and re-adapted by Atif Aziz (www.raboof.com)
+ ///
+
+ [ Serializable ]
+ internal class JArray : CollectionBase, IJayrockCompatible
+ {
+ public JArray() {}
+
+ public JArray(IEnumerable collection)
+ {
+ foreach (object item in collection)
+ List.Add(item);
+ }
+
+ public virtual object this[int index]
+ {
+ get { return InnerList[index]; }
+ set { List[index] = value; }
+ }
+
+ public int Length
+ {
+ get { return Count; }
+ }
+
+ public JArray Put(object value)
+ {
+ Add(value);
+ return this;
+ }
+
+ public virtual void Add(object value)
+ {
+ List.Add(value);
+ }
+ public virtual void Add(int index, object value)
+ {
+ InnerList.Insert(index, value);
+ }
+
+ public virtual void Remove(object value)
+ {
+ List.Remove(value);
+ }
+
+ public virtual bool Contains(object value)
+ {
+ return List.Contains(value);
+ }
+
+ public virtual int IndexOf(object value)
+ {
+ return List.IndexOf(value);
+ }
+
+ public virtual bool HasValueAt(int index)
+ {
+ return this[index] != null;
+ }
+
+ public virtual object GetValue(int index)
+ {
+ return GetValue(index, null);
+ }
+
+ public virtual object GetValue(int index, object defaultValue)
+ {
+ object value = this[index];
+ return value != null ? value : defaultValue;
+ }
+
+ public virtual bool GetBoolean(int index)
+ {
+ return GetBoolean(index, false);
+ }
+
+ public virtual bool GetBoolean(int index, bool defaultValue)
+ {
+ object value = GetValue(index);
+ if (value == null) return defaultValue;
+ return Convert.ToBoolean(value, CultureInfo.InvariantCulture);
+ }
+
+ public virtual double GetDouble(int index)
+ {
+ return GetDouble(index, float.NaN);
+ }
+
+ public virtual double GetDouble(int index, float defaultValue)
+ {
+ object value = GetValue(index);
+ if (value == null) return defaultValue;
+ return Convert.ToDouble(value, CultureInfo.InvariantCulture);
+ }
+
+ public virtual int GetInt32(int index)
+ {
+ return GetInt32(index, 0);
+ }
+
+ public virtual int GetInt32(int index, int defaultValue)
+ {
+ object value = GetValue(index);
+ if (value == null) return defaultValue;
+ return Convert.ToInt32(value, CultureInfo.InvariantCulture);
+ }
+
+ public virtual string GetString(int index)
+ {
+ return GetString(index, string.Empty);
+ }
+
+ public virtual string GetString(int index, string defaultValue)
+ {
+ object value = GetValue(index);
+ if (value == null) return defaultValue;
+ return value.ToString();
+ }
+
+ public virtual JArray GetArray(int index)
+ {
+ return (JArray) GetValue(index);
+ }
+
+ public virtual JObject GetObject(int index)
+ {
+ return (JObject) GetValue(index);
+ }
+
+ protected override void OnValidate(object value)
+ {
+ //
+ // Null values are allowed in a JSON array so don't delegate
+ // to the base class (CollectionBase) implementation since that
+ // disallows null entries by default.
+ //
+ }
+
+ ///
+ /// Make an JSON external form string of this JsonArray. For
+ /// compactness, no unnecessary whitespace is added.
+ ///
+ ///
+ /// This method assumes that the data structure is acyclical.
+ ///
+
+ public override string ToString()
+ {
+ return TextJsonSerializer.SerializeToJayrockCompatibleJson(this);
+ }
+
+
+ ///
+ /// Copies the elements to a new object array.
+ ///
+
+ public virtual object[] ToArray()
+ {
+ return (object[]) ToArray(typeof(object));
+ }
+
+ ///
+ /// Copies the elements to a new array of the specified type.
+ ///
+
+ public virtual Array ToArray(Type elementType)
+ {
+ return InnerList.ToArray(elementType);
+ }
+
+ public virtual void Reverse()
+ {
+ InnerList.Reverse();
+ }
+
+ //
+ // Methods that imitate the JavaScript array methods.
+ //
+
+ ///
+ /// Appends new elements to an array.
+ ///
+ ///
+ /// The new length of the array.
+ ///
+ ///
+ /// This method appends elements in the order in which they appear. If
+ /// one of the arguments is an array, it is added as a single element.
+ /// Use the method to join the elements from two or
+ /// more arrays.
+ ///
+
+ public int Push(object value)
+ {
+ Add(value);
+ return Count;
+ }
+
+ ///
+ /// Appends new elements to an array.
+ ///
+ ///
+ /// The new length of the array.
+ ///
+ ///
+ /// This method appends elements in the order in which they appear. If
+ /// one of the arguments is an array, it is added as a single element.
+ /// Use the method to join the elements from two or
+ /// more arrays.
+ ///
+
+ public int Push(params object[] values)
+ {
+ if (values != null)
+ {
+ foreach (object value in values)
+ Push(value);
+ }
+
+ return Count;
+ }
+
+ ///
+ /// Removes the last element from an array and returns it.
+ ///
+ ///
+ /// If the array is empty, null is returned.
+ ///
+
+ public object Pop()
+ {
+ if (Count == 0)
+ return null;
+
+ object lastValue = InnerList[Count - 1];
+ RemoveAt(Count - 1);
+ return lastValue;
+ }
+
+ ///
+ /// Returns a new array consisting of a combination of two or more
+ /// arrays.
+ ///
+
+ public JArray Concat(params object[] values)
+ {
+ JArray newArray = new JArray(this);
+
+ if (values != null)
+ {
+ foreach (object value in values)
+ {
+ JArray arrayValue = value as JArray;
+
+ if (arrayValue != null)
+ {
+ foreach (object arrayValueValue in arrayValue)
+ newArray.Push(arrayValueValue);
+ }
+ else
+ {
+ newArray.Push(value);
+ }
+ }
+ }
+
+ return newArray;
+ }
+
+ ///
+ /// Removes the first element from an array and returns it.
+ ///
+
+ public object Shift()
+ {
+ if (Count == 0)
+ return null;
+
+ object firstValue = InnerList[0];
+ RemoveAt(0);
+ return firstValue;
+ }
+
+ ///
+ /// Returns an array with specified elements inserted at the beginning.
+ ///
+ ///
+ /// The unshift method inserts elements into the start of an array, so
+ /// they appear in the same order in which they appear in the argument
+ /// list.
+ ///
+
+ public JArray Unshift(object value)
+ {
+ List.Insert(0, value);
+ return this;
+ }
+
+ ///
+ /// Returns an array with specified elements inserted at the beginning.
+ ///
+ ///
+ /// The unshift method inserts elements into the start of an array, so
+ /// they appear in the same order in which they appear in the argument
+ /// list.
+ ///
+
+ public JArray Unshift(params object[] values)
+ {
+ if (values != null)
+ {
+ foreach (object value in values)
+ Unshift(value);
+ }
+
+ return this;
+ }
+ }
+}
diff --git a/dotnet/src/dotnetcore/GxClasses/Domain/JNull.cs b/dotnet/src/dotnetcore/GxClasses/Domain/JNull.cs
new file mode 100644
index 000000000..434ad643d
--- /dev/null
+++ b/dotnet/src/dotnetcore/GxClasses/Domain/JNull.cs
@@ -0,0 +1,87 @@
+#region License, Terms and Conditions
+//
+// Jayrock - A JSON-RPC implementation for the Microsoft .NET Framework
+// Written by Atif Aziz (atif.aziz@skybow.com)
+// Copyright (c) Atif Aziz. All rights reserved.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License as published by the Free
+// Software Foundation; either version 2.1 of the License, or (at your option)
+// any later version.
+//
+// This library is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+// FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+// details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this library; if not, write to the Free Software Foundation, Inc.,
+// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+#endregion
+
+namespace GeneXus.Application
+{
+ #region Imports
+
+ using System;
+ //using System.Data.SqlTypes;
+ #endregion
+
+ ///
+ /// Represent the one and only representation of the "null" value in JSON.
+ ///
+
+ internal interface IJayrockCompatible
+ {
+
+ }
+ [ Serializable ]
+ internal sealed class JNull : IJayrockCompatible
+ {
+ public static readonly JNull Value = new JNull();
+
+ public override string ToString()
+ {
+ return "null";
+ }
+
+ public static bool LogicallyEquals(object o)
+ {
+ //
+ // Equals a null reference.
+ //
+
+ if (o == null)
+ return true;
+
+ //
+ // Equals self, of course.
+ //
+
+ if (o.Equals(JNull.Value))
+ return true;
+
+ //
+ // Equals the logical null value used in database applications.
+ //
+
+ //if (Convert.IsDBNull(o))
+ // return true;
+
+ //
+ // Equals any type that supports logical nullability and whose
+ // current state is logically null.
+ //
+
+ //INullable nullable = o as INullable;
+
+ // if (nullable == null)
+ return false;
+
+ //return nullable.IsNull;
+ }
+
+ private JNull() {}
+ }
+}
diff --git a/dotnet/src/dotnetcore/GxClasses/Domain/JObject.cs b/dotnet/src/dotnetcore/GxClasses/Domain/JObject.cs
new file mode 100644
index 000000000..e1aadfc30
--- /dev/null
+++ b/dotnet/src/dotnetcore/GxClasses/Domain/JObject.cs
@@ -0,0 +1,123 @@
+// the terms of the GNU Lesser General Public License as published by the Free
+// Software Foundation; either version 2.1 of the License, or (at your option)
+// any later version.
+//
+// This library is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+// FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+using System;
+using System.Collections;
+using System.Collections.Specialized;
+using GeneXus.Utils;
+namespace GeneXus.Application
+{
+ /// the implementation does internally try to remember the order in which
+ /// the keys were added in order facilitate human-readability as in when
+ /// an instance is rendered as text.
+
+ [ Serializable ]
+ internal class JObject : OrderedDictionary, IJayrockCompatible
+ {
+ public JObject() {}
+
+ ///
+ /// Construct a JObject from a IDictionary
+ ///
+
+ public JObject(IDictionary members)
+ {
+ foreach (DictionaryEntry entry in members)
+ {
+ if (entry.Key == null)
+ throw new Exception("InvalidMemberException");
+
+ base.Add(entry.Key.ToString(), entry.Value);
+ }
+
+ }
+
+ public virtual bool HasMembers
+ {
+ get { return Count > 0; }
+ }
+
+
+ ///
+ /// Accumulate values under a key. It is similar to the Put method except
+ /// that if there is already an object stored under the key then a
+ /// JArray is stored under the key to hold all of the accumulated values.
+ /// If there is already a JArray, then the new value is appended to it.
+ /// In contrast, the Put method replaces the previous value.
+ ///
+
+ public virtual JObject Accumulate(string name, object value)
+ {
+ if (name == null)
+ throw new ArgumentNullException(nameof(name));
+
+ object current = base[name];
+
+ if (current == null)
+ {
+ Put(name, value);
+ }
+ else
+ {
+ IList values = current as IList;
+
+ if (values != null)
+ {
+ values.Add(value);
+ }
+ else
+ {
+ values = new JArray
+ {
+ current,
+ value
+ };
+ Put(name, values);
+ }
+ }
+
+ return this;
+ }
+
+ ///
+ /// Put a key/value pair in the JObject. If the value is null,
+ /// then the key will be removed from the JObject if it is present.
+ ///
+
+ public virtual JObject Put(string name, object value)
+ {
+ if (name == null)
+ throw new ArgumentNullException(nameof(name));
+
+ if (value != null)
+ this[name] = value;
+ else
+ Remove(name);
+
+ return this;
+ }
+
+ public virtual ICollection Names
+ {
+ get
+ {
+ return base.Keys;
+ }
+ }
+
+ ///
+ /// Overridden to return a JSON formatted object as a string.
+ ///
+
+ public override string ToString()
+ {
+ return TextJsonSerializer.SerializeToJayrockCompatibleJson(this);
+ }
+
+
+ }
+}
diff --git a/dotnet/src/dotnetcore/GxClasses/GxClasses.csproj b/dotnet/src/dotnetcore/GxClasses/GxClasses.csproj
index 43b211dc5..be360c140 100644
--- a/dotnet/src/dotnetcore/GxClasses/GxClasses.csproj
+++ b/dotnet/src/dotnetcore/GxClasses/GxClasses.csproj
@@ -1,4 +1,4 @@
-
+
net6.0;net8.0
@@ -151,31 +151,31 @@
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
+
-
+
diff --git a/dotnet/src/dotnetcore/GxClasses/Helpers/BasicAuthentication.cs b/dotnet/src/dotnetcore/GxClasses/Helpers/BasicAuthentication.cs
index f6d093183..d7cdc1ca2 100644
--- a/dotnet/src/dotnetcore/GxClasses/Helpers/BasicAuthentication.cs
+++ b/dotnet/src/dotnetcore/GxClasses/Helpers/BasicAuthentication.cs
@@ -1,11 +1,11 @@
-using System;
-using System.Collections.Generic;
+using System;
using System.Text;
namespace GxClasses.Helpers
{
public class BasicAuthenticationHeaderValue
{
+ const char UserNamePasswordSeparator= ':';
public BasicAuthenticationHeaderValue(string authenticationHeaderValue)
{
if (!string.IsNullOrWhiteSpace(authenticationHeaderValue))
@@ -19,7 +19,7 @@ public BasicAuthenticationHeaderValue(string authenticationHeaderValue)
}
private readonly string _authenticationHeaderValue;
- private string[] _splitDecodedCredentials;
+ private string _usernamePassword;
public bool IsValidBasicAuthenticationHeaderValue { get; private set; }
public string UserIdentifier { get; private set; }
@@ -32,11 +32,11 @@ private bool TryDecodeHeaderValue()
{
return false;
}
- var encodedCredentials = _authenticationHeaderValue.Substring(headerSchemeLength);
+ string encodedCredentials = _authenticationHeaderValue.Substring(headerSchemeLength);
try
{
- var decodedCredentials = Convert.FromBase64String(encodedCredentials);
- _splitDecodedCredentials = System.Text.Encoding.ASCII.GetString(decodedCredentials).Split(':');
+ byte[] decodedCredentials = Convert.FromBase64String(encodedCredentials);
+ _usernamePassword = Encoding.ASCII.GetString(decodedCredentials);
return true;
}
catch (FormatException)
@@ -47,13 +47,19 @@ private bool TryDecodeHeaderValue()
private void ReadAuthenticationHeaderValue()
{
- IsValidBasicAuthenticationHeaderValue = _splitDecodedCredentials!= null && _splitDecodedCredentials.Length == 2
- && !string.IsNullOrWhiteSpace(_splitDecodedCredentials[0])
- && !string.IsNullOrWhiteSpace(_splitDecodedCredentials[1]);
+ IsValidBasicAuthenticationHeaderValue = !string.IsNullOrEmpty(_usernamePassword) && _usernamePassword.Contains(UserNamePasswordSeparator);
if (IsValidBasicAuthenticationHeaderValue)
{
- UserIdentifier = _splitDecodedCredentials[0];
- UserPassword = _splitDecodedCredentials[1];
+ int separatorIndex = _usernamePassword.IndexOf(UserNamePasswordSeparator);
+ UserIdentifier = _usernamePassword.Substring(0, separatorIndex);
+ if (separatorIndex + 1 < _usernamePassword.Length)
+ {
+ UserPassword = _usernamePassword.Substring(separatorIndex + 1);
+ }
+ else
+ {
+ UserPassword = string.Empty;
+ }
}
}
}
diff --git a/dotnet/src/dotnetcore/GxClasses/Helpers/GXGeographyCore.cs b/dotnet/src/dotnetcore/GxClasses/Helpers/GXGeographyCore.cs
index 80d80a9e7..af51f7be6 100644
--- a/dotnet/src/dotnetcore/GxClasses/Helpers/GXGeographyCore.cs
+++ b/dotnet/src/dotnetcore/GxClasses/Helpers/GXGeographyCore.cs
@@ -2,7 +2,12 @@
using System.Collections;
using System.Globalization;
using System.Runtime.Serialization;
+using System.Text.Json.Serialization;
+#if NETCORE
+using GeneXus.Application;
+#else
using Jayrock.Json;
+#endif
using GeographicLib;
using NetTopologySuite.Geometries;
using NetTopologySuite.IO;
@@ -201,6 +206,7 @@ internal static object STRingN(object instance, int i)
[KnownType(typeof(System.Double[]))]
[KnownType(typeof(System.Collections.ArrayList))]
[DataContract]
+ [JsonConverter(typeof(CustomGeospatialConverter))]
public class Geospatial : IGeographicNative
{
static readonly IGXLogger log = GXLoggerFactory.GetLogger();
@@ -405,7 +411,7 @@ public double Latitude
}
}
- public String JSONPointToWKT(JArray coords)
+ internal String JSONPointToWKT(JArray coords)
{
String[] jbuffer = new String[] { "", "" };
jbuffer[0] = "";
diff --git a/dotnet/src/dotnetcore/GxClasses/Properties/AssemblyInfo.cs b/dotnet/src/dotnetcore/GxClasses/Properties/AssemblyInfo.cs
index 1eeded06b..1c8dea312 100644
--- a/dotnet/src/dotnetcore/GxClasses/Properties/AssemblyInfo.cs
+++ b/dotnet/src/dotnetcore/GxClasses/Properties/AssemblyInfo.cs
@@ -14,5 +14,7 @@
[assembly: InternalsVisibleTo("GeneXus.Deploy.AzureFunctions.Handlers")]
[assembly: InternalsVisibleTo("AzureFunctionsTest")]
[assembly: InternalsVisibleTo("GXMessageBroker")]
+[assembly: InternalsVisibleTo("GeneXus.SD.Store.StoreManager")]
[assembly: InternalsVisibleTo("DotNetCoreChunkedTest")]
-[assembly: InternalsVisibleTo("GeneXus.OpenTelemetry.Diagnostics")]
\ No newline at end of file
+[assembly: InternalsVisibleTo("DotNetCoreChunkedTest")]
+[assembly: InternalsVisibleTo("GeneXus.OpenTelemetry.Diagnostics")]
diff --git a/dotnet/src/dotnetcore/GxClasses/Services/LogService/GXLogService.cs b/dotnet/src/dotnetcore/GxClasses/Services/LogService/GXLogService.cs
index cc455dbdc..a015eca6d 100644
--- a/dotnet/src/dotnetcore/GxClasses/Services/LogService/GXLogService.cs
+++ b/dotnet/src/dotnetcore/GxClasses/Services/LogService/GXLogService.cs
@@ -1,3 +1,4 @@
+using System;
using GeneXus.Configuration;
using Microsoft.Extensions.Logging;
@@ -7,15 +8,25 @@ public static class GXLogService
{
private static string AZURE_APPLICATION_INSIGHTS_LOG = "AZUREAPPLICATIONINSIGHTS";
const string OTEL_AZUREMONITOR_EXPORTER = "OTEL_AZUREMONITOR_EXPORTER";
+ const string OPENTELEMETRY = "OPENTELEMETRY";
+ internal static string OTEL_LOGS_EXPORTER = "OTEL_LOGS_EXPORTER";
const string LOG_OUTPUT = "LOG_OUTPUT";
+
public static ILoggerFactory GetLogFactory()
{
- if (Config.GetValueOf(LOG_OUTPUT, out string logProvider))
- {
- if (logProvider == OTEL_AZUREMONITOR_EXPORTER)
- return AzureAppInsightsLogProvider.GetAzureMonitorLoggerFactory();
- else if (logProvider == AZURE_APPLICATION_INSIGHTS_LOG)
- return AzureAppInsightsLogProvider.GetLoggerFactory();
+ string otelLogEnvVar = Environment.GetEnvironmentVariable(OTEL_LOGS_EXPORTER);
+ if (string.IsNullOrEmpty(otelLogEnvVar) || !otelLogEnvVar.ToLower().Equals("none"))
+ {
+ if (Config.GetValueOf(LOG_OUTPUT, out string logProvider))
+ {
+ if (logProvider == OTEL_AZUREMONITOR_EXPORTER)
+ return OpentelemetryLogProvider.GetAzureMonitorLoggerFactory();
+ else if (logProvider == AZURE_APPLICATION_INSIGHTS_LOG)
+ return OpentelemetryLogProvider.GetAzureAppInsightsLoggerFactory();
+ else
+ if (logProvider == OPENTELEMETRY)
+ return OpentelemetryLogProvider.GetOpentelemetryLoggerFactory();
+ }
}
return null;
}
diff --git a/dotnet/src/dotnetcore/GxClasses/Services/LogService/AzureAppInsights/AzureAppInsightsLogProvider.cs b/dotnet/src/dotnetcore/GxClasses/Services/LogService/OpenTelemetry/OtelLogProvider.cs
similarity index 64%
rename from dotnet/src/dotnetcore/GxClasses/Services/LogService/AzureAppInsights/AzureAppInsightsLogProvider.cs
rename to dotnet/src/dotnetcore/GxClasses/Services/LogService/OpenTelemetry/OtelLogProvider.cs
index 8a1e4318c..20fef179b 100644
--- a/dotnet/src/dotnetcore/GxClasses/Services/LogService/AzureAppInsights/AzureAppInsightsLogProvider.cs
+++ b/dotnet/src/dotnetcore/GxClasses/Services/LogService/OpenTelemetry/OtelLogProvider.cs
@@ -1,11 +1,15 @@
using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
using Azure.Monitor.OpenTelemetry.Exporter;
using Microsoft.Extensions.Logging;
using OpenTelemetry.Logs;
+using OpenTelemetry.Resources;
+using OpenTelemetry.Exporter;
namespace GeneXus.Services.Log
{
- public class AzureAppInsightsLogProvider : ILoggerFactory
+ public class OpentelemetryLogProvider : ILoggerFactory
{
private static string APPLICATIONINSIGHTS_CONNECTION_STRING = "APPLICATIONINSIGHTS_CONNECTION_STRING";
private const string LOG_LEVEL_ENVVAR = "GX_LOG_LEVEL";
@@ -25,7 +29,8 @@ public static ILoggerFactory GetAzureMonitorLoggerFactory()
builder.AddOpenTelemetry(options =>
{
options.AddAzureMonitorLogExporter(o => o.ConnectionString = appInsightsConnection);
- options.AddConsoleExporter();
+ if (GenerateOtelLogsToConsole())
+ options.AddConsoleExporter();
}).SetMinimumLevel(loglevel);
});
}
@@ -42,7 +47,7 @@ public static ILoggerFactory GetAzureMonitorLoggerFactory()
return loggerFactory;
}
- public static ILoggerFactory GetLoggerFactory()
+ public static ILoggerFactory GetAzureAppInsightsLoggerFactory()
{
string appInsightsConnection = Environment.GetEnvironmentVariable(APPLICATIONINSIGHTS_CONNECTION_STRING);
try
@@ -71,6 +76,25 @@ public static ILoggerFactory GetLoggerFactory()
return loggerFactory;
}
+
+ public static ILoggerFactory GetOpentelemetryLoggerFactory()
+ {
+ LogLevel loglevel = GetLogLevel();
+ loggerFactory = LoggerFactory.Create(builder => builder.AddOpenTelemetry(logging =>
+ {
+ var resourceBuilder = ResourceBuilder.CreateDefault()
+ .AddTelemetrySdk();
+
+ logging.SetResourceBuilder(resourceBuilder)
+ .AddOtlpExporter();
+
+ if (GenerateOtelLogsToConsole())
+ logging.AddConsoleExporter();
+
+ })
+ .SetMinimumLevel(loglevel));
+ return loggerFactory;
+ }
private static LogLevel GetLogLevel()
{
string loglevelvalue = Environment.GetEnvironmentVariable(LOG_LEVEL_ENVVAR);
@@ -79,7 +103,7 @@ private static LogLevel GetLogLevel()
{
if (!Enum.TryParse(loglevelvalue, out loglevel))
{
- CustomLogLevel customLogLevel = CustomLogLevel.Info;
+ CustomLogLevel customLogLevel = CustomLogLevel.info;
if (Enum.TryParse(loglevelvalue, out customLogLevel))
{
loglevel = toLogLevel(customLogLevel);
@@ -91,6 +115,13 @@ private static LogLevel GetLogLevel()
return loglevel;
}
+ private static bool GenerateOtelLogsToConsole()
+ {
+ string otelLogsEnvVar = Environment.GetEnvironmentVariable(GXLogService.OTEL_LOGS_EXPORTER);
+ if (string.IsNullOrEmpty(otelLogsEnvVar)) { return false; }
+ return otelLogsEnvVar.ToLower().Contains("console") || otelLogsEnvVar.ToLower().Contains("logging");
+ }
+
public void AddProvider(ILoggerProvider provider)
{
loggerFactory.AddProvider(provider);
@@ -105,25 +136,25 @@ public ILogger CreateLogger(string name)
}
private enum CustomLogLevel
{
- None,
- All,
- Debug,
- Info,
- Warn,
- Error,
- Fatal
+ none,
+ all,
+ debug,
+ info,
+ warn,
+ error,
+ fatal
}
private static LogLevel toLogLevel(CustomLogLevel customLogLevel)
{
switch (customLogLevel)
{
- case CustomLogLevel.None: return LogLevel.None;
- case CustomLogLevel.All: return LogLevel.Trace;
- case CustomLogLevel.Debug: return LogLevel.Debug;
- case CustomLogLevel.Info: return LogLevel.Information;
- case CustomLogLevel.Warn: return LogLevel.Warning;
- case CustomLogLevel.Error: return LogLevel.Error;
- case CustomLogLevel.Fatal: return LogLevel.Critical;
+ case CustomLogLevel.none: return LogLevel.None;
+ case CustomLogLevel.all: return LogLevel.Trace;
+ case CustomLogLevel.debug: return LogLevel.Debug;
+ case CustomLogLevel.info: return LogLevel.Information;
+ case CustomLogLevel.warn: return LogLevel.Warning;
+ case CustomLogLevel.error: return LogLevel.Error;
+ case CustomLogLevel.fatal: return LogLevel.Critical;
default: return LogLevel.Information;
}
}
diff --git a/dotnet/src/dotnetcore/GxClasses/Services/OpenTelemetry/OpenTelemetryService.cs b/dotnet/src/dotnetcore/GxClasses/Services/OpenTelemetry/OpenTelemetryService.cs
index 3c8cacf39..3dfdf2b00 100644
--- a/dotnet/src/dotnetcore/GxClasses/Services/OpenTelemetry/OpenTelemetryService.cs
+++ b/dotnet/src/dotnetcore/GxClasses/Services/OpenTelemetry/OpenTelemetryService.cs
@@ -1,4 +1,5 @@
using System;
+using System.Text.RegularExpressions;
using GxClasses.Helpers;
namespace GeneXus.Services.OpenTelemetry
@@ -10,11 +11,56 @@ public interface IOpenTelemetryProvider
public static class OpenTelemetryService
{
- private static readonly IGXLogger log = GXLoggerFactory.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName);
+ private static readonly IGXLogger log = GXLoggerFactory.GetLogger(typeof(OpenTelemetryService).FullName);
- private static string OPENTELEMETRY_SERVICE = "Observability";
- public static string GX_ACTIVITY_SOURCE_NAME = "GeneXus.Tracing";
-
+ const string OTEL_RESOURCE_ATTRIBUTES = "OTEL_RESOURCE_ATTRIBUTES";
+ const string OTEL_SERVICE_NAME = "OTEL_SERVICE_NAME";
+ const string OTEL_SERVICE_VERSION = "OTEL_SERVICE_VERSION";
+ const string OPENTELEMETRY_SERVICE = "Observability";
+
+ public static string GX_ACTIVITY_SOURCE_NAME= GetServiceNameValue(OTEL_RESOURCE_ATTRIBUTES);
+ public static string GX_ACTIVITY_SOURCE_VERSION= GetServiceVersionValue(OTEL_RESOURCE_ATTRIBUTES);
+
+ private static string GetServiceNameValue(string input)
+ {
+ string otelServiceNameEnvVar = Environment.GetEnvironmentVariable(OTEL_SERVICE_NAME);
+ if (!string.IsNullOrEmpty(otelServiceNameEnvVar))
+ return otelServiceNameEnvVar;
+
+ string pattern = @"(?:\b\w+\b=\w+)(?:,(?:\b\w+\b=\w+))*";
+ MatchCollection matches = Regex.Matches(input, pattern);
+
+ foreach (Match match in matches)
+ {
+ string[] keyValue = match.Value.Split('=');
+
+ if (keyValue[0] == "service.name")
+ {
+ return keyValue[1];
+ }
+ }
+ return "GeneXus.Tracing";
+ }
+ private static string GetServiceVersionValue(string input)
+ {
+ string otelServiceNameEnvVar = Environment.GetEnvironmentVariable(OTEL_SERVICE_VERSION);
+ if (!string.IsNullOrEmpty(otelServiceNameEnvVar))
+ return otelServiceNameEnvVar;
+
+ string pattern = @"(?:\b\w+\b=\w+)(?:,(?:\b\w+\b=\w+))*";
+ MatchCollection matches = Regex.Matches(input, pattern);
+
+ foreach (Match match in matches)
+ {
+ string[] keyValue = match.Value.Split('=');
+
+ if (keyValue[0] == "service.version")
+ {
+ return keyValue[1];
+ }
+ }
+ return string.Empty;
+ }
private static IOpenTelemetryProvider GetOpenTelemetryProvider()
{
IOpenTelemetryProvider otelProvider = null;
@@ -34,7 +80,7 @@ private static IOpenTelemetryProvider GetOpenTelemetryProvider()
}
catch (Exception e)
{
- GXLogging.Error(log, "Couldn´t create OpenTelemetry provider.", e.Message, e);
+ GXLogging.Error(log, "Couldn't create OpenTelemetry provider.", e.Message, e);
throw e;
}
}
diff --git a/dotnet/src/dotnetcore/GxDataInitialization/GXDataInitialization.csproj b/dotnet/src/dotnetcore/GxDataInitialization/GXDataInitialization.csproj
index 886477ee5..31547472d 100644
--- a/dotnet/src/dotnetcore/GxDataInitialization/GXDataInitialization.csproj
+++ b/dotnet/src/dotnetcore/GxDataInitialization/GXDataInitialization.csproj
@@ -12,6 +12,10 @@
+
+
+
+
diff --git a/dotnet/src/dotnetcore/GxExcel/GxExcel.csproj b/dotnet/src/dotnetcore/GxExcel/GxExcel.csproj
index 775944fc7..916d3c26d 100644
--- a/dotnet/src/dotnetcore/GxExcel/GxExcel.csproj
+++ b/dotnet/src/dotnetcore/GxExcel/GxExcel.csproj
@@ -16,8 +16,9 @@
+
-
+
diff --git a/dotnet/src/dotnetcore/GxMail/GxMail.csproj b/dotnet/src/dotnetcore/GxMail/GxMail.csproj
index 61616d6d9..b9210f368 100644
--- a/dotnet/src/dotnetcore/GxMail/GxMail.csproj
+++ b/dotnet/src/dotnetcore/GxMail/GxMail.csproj
@@ -66,6 +66,7 @@
+
diff --git a/dotnet/src/dotnetcore/GxMaps/GxMaps.csproj b/dotnet/src/dotnetcore/GxMaps/GxMaps.csproj
index bf243af4a..dc95fa2c3 100644
--- a/dotnet/src/dotnetcore/GxMaps/GxMaps.csproj
+++ b/dotnet/src/dotnetcore/GxMaps/GxMaps.csproj
@@ -11,17 +11,11 @@
-
-
-
- False
- ..\libs\net8.0\Jayrock.dll
-
-
- False
- ..\libs\Jayrock.dll
-
-
-
+
+
+
-
\ No newline at end of file
+
+
+
+
\ No newline at end of file
diff --git a/dotnet/src/dotnetcore/GxNetCoreStartup/CsrfHelper.cs b/dotnet/src/dotnetcore/GxNetCoreStartup/CsrfHelper.cs
index 2306f028e..2020e7371 100644
--- a/dotnet/src/dotnetcore/GxNetCoreStartup/CsrfHelper.cs
+++ b/dotnet/src/dotnetcore/GxNetCoreStartup/CsrfHelper.cs
@@ -13,13 +13,15 @@ public class ValidateAntiForgeryTokenMiddleware
static readonly IGXLogger log = GXLoggerFactory.GetLogger();
private readonly RequestDelegate _next;
private readonly IAntiforgery _antiforgery;
+ private string _restBasePath;
private string _basePath;
- public ValidateAntiForgeryTokenMiddleware(RequestDelegate next, IAntiforgery antiforgery, String basePath)
+ public ValidateAntiForgeryTokenMiddleware(RequestDelegate next, IAntiforgery antiforgery, string basePath)
{
_next = next;
_antiforgery = antiforgery;
- _basePath = "/" + basePath;
+ _restBasePath = $"{basePath}{Startup.REST_BASE_URL}";
+ _basePath = $"/{basePath}";
}
public async Task Invoke(HttpContext context)
@@ -42,9 +44,13 @@ public async Task Invoke(HttpContext context)
SetAntiForgeryTokens(_antiforgery, context);
}
}
- if (!context.Request.Path.Value.EndsWith(_basePath)) //VerificationToken
+ if (!IsVerificationTokenServiceRequest(context))
await _next(context);
}
+ private bool IsVerificationTokenServiceRequest(HttpContext context)
+ {
+ return context.Request.Path.Value.EndsWith(_restBasePath);
+ }
internal static void SetAntiForgeryTokens(IAntiforgery _antiforgery, HttpContext context)
{
AntiforgeryTokenSet tokenSet = _antiforgery.GetAndStoreTokens(context);
diff --git a/dotnet/src/dotnetcore/GxNetCoreStartup/GxNetCoreStartup.csproj b/dotnet/src/dotnetcore/GxNetCoreStartup/GxNetCoreStartup.csproj
index 9b0bd19a9..172d64c86 100644
--- a/dotnet/src/dotnetcore/GxNetCoreStartup/GxNetCoreStartup.csproj
+++ b/dotnet/src/dotnetcore/GxNetCoreStartup/GxNetCoreStartup.csproj
@@ -10,19 +10,18 @@
-
-
-
+
+
-
+
-
+
-
-
-
-
-
+
+
+
+
+
@@ -33,17 +32,6 @@
-
-
- False
- ..\libs\net8.0\Jayrock.dll
-
-
- False
- ..\libs\Jayrock.dll
-
-
-
diff --git a/dotnet/src/dotnetcore/GxNetCoreStartup/Startup.cs b/dotnet/src/dotnetcore/GxNetCoreStartup/Startup.cs
index 5cda19ae5..b90fd4050 100644
--- a/dotnet/src/dotnetcore/GxNetCoreStartup/Startup.cs
+++ b/dotnet/src/dotnetcore/GxNetCoreStartup/Startup.cs
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.IO;
using System.Net;
+using System.Reflection;
using System.Threading.Tasks;
using GeneXus.Configuration;
using GeneXus.Http;
@@ -18,6 +19,7 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
+using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Rewrite;
using Microsoft.AspNetCore.Routing;
@@ -133,7 +135,7 @@ public class Startup
const string RESOURCES_FOLDER = "Resources";
const string TRACE_FOLDER = "logs";
const string TRACE_PATTERN = "trace.axd";
- const string REST_BASE_URL = "rest/";
+ internal const string REST_BASE_URL = "rest/";
const string DATA_PROTECTION_KEYS = "DataProtection-Keys";
const string REWRITE_FILE = "rewrite.config";
const string SWAGGER_DEFAULT_YAML = "default.yaml";
@@ -142,6 +144,7 @@ public class Startup
const string CORS_POLICY_NAME = "AllowSpecificOriginsPolicy";
const string CORS_ANY_ORIGIN = "*";
const double CORS_MAX_AGE_SECONDS = 86400;
+ internal const string GX_CONTROLLERS = "gxcontrollers";
public List servicesBase = new List();
@@ -160,11 +163,33 @@ public void ConfigureServices(IServiceCollection services)
{
OpenTelemetryService.Setup(services);
- services.AddMvc(option => option.EnableEndpointRouting = false);
+ services.AddControllers();
+ string controllers = Path.Combine(Startup.LocalPath, "bin", GX_CONTROLLERS);
+ IMvcBuilder mvcBuilder = services.AddMvc(option => option.EnableEndpointRouting = false);
+ try
+ {
+ if (Directory.Exists(controllers))
+ {
+ foreach (string controller in Directory.GetFiles(controllers))
+ {
+ Console.WriteLine($"Loading controller {controller}");
+ mvcBuilder.AddApplicationPart(Assembly.LoadFrom(controller)).AddControllersAsServices();
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Console.Error.WriteLine("Error loading gxcontrollers " + ex.Message);
+ }
services.Configure(options =>
{
options.AllowSynchronousIO = true;
options.Limits.MaxRequestBodySize = null;
+ if (Config.GetValueOrEnvironmentVarOf("MinRequestBodyDataRate", out string MinRequestBodyDataRateStr) && double.TryParse(MinRequestBodyDataRateStr, out double MinRequestBodyDataRate))
+ {
+ GXLogging.Info(log, $"MinRequestBodyDataRate:{MinRequestBodyDataRate}");
+ options.Limits.MinRequestBodyDataRate = new MinDataRate(bytesPerSecond: MinRequestBodyDataRate, gracePeriod: TimeSpan.FromSeconds(10));
+ }
});
services.Configure(options =>
{
@@ -196,6 +221,12 @@ public void ConfigureServices(IServiceCollection services)
options.Cookie.HttpOnly = true;
options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
options.Cookie.IsEssential = true;
+ string sessionCookieName = GxWebSession.GetSessionCookieName(VirtualPath);
+ if (!string.IsNullOrEmpty(sessionCookieName))
+ {
+ options.Cookie.Name=sessionCookieName;
+ GxWebSession.SessionCookieName = sessionCookieName;
+ }
string sameSite;
SameSiteMode sameSiteMode = SameSiteMode.Unspecified;
if (Config.GetValueOf("SAMESITE_COOKIE", out sameSite) && Enum.TryParse(sameSite, out sameSiteMode))
@@ -209,7 +240,7 @@ public void ConfigureServices(IServiceCollection services)
services.AddAntiforgery(options =>
{
options.HeaderName = HttpHeader.X_CSRF_TOKEN_HEADER;
- options.SuppressXFrameOptionsHeader = false;
+ options.SuppressXFrameOptionsHeader = true;
});
}
services.AddDirectoryBrowser();
@@ -236,7 +267,6 @@ public void ConfigureServices(IServiceCollection services)
});
}
DefineCorsPolicy(services);
- services.AddMvc();
}
private void DefineCorsPolicy(IServiceCollection services)
@@ -317,10 +347,12 @@ public void Configure(IApplicationBuilder app, Microsoft.AspNetCore.Hosting.IHos
provider.Mappings[".usdz"] = "model/vnd.pixar.usd";
provider.Mappings[".sfb"] = "model/sfb";
provider.Mappings[".gltf"] = "model/gltf+json";
+ provider.Mappings[".ini"] = "text/plain";
if (GXUtil.CompressResponse())
{
app.UseResponseCompression();
}
+ app.UseRouting();
app.UseCookiePolicy();
app.UseSession();
app.UseStaticFiles();
@@ -343,6 +375,10 @@ public void Configure(IApplicationBuilder app, Microsoft.AspNetCore.Hosting.IHos
app.UseHttpsRedirection();
app.UseHsts();
}
+ app.UseEndpoints(endpoints =>
+ {
+ endpoints.MapControllers();
+ });
if (log.IsDebugEnabled)
{
try
@@ -411,7 +447,7 @@ public void Configure(IApplicationBuilder app, Microsoft.AspNetCore.Hosting.IHos
if (RestAPIHelpers.ValidateCsrfToken())
{
antiforgery = app.ApplicationServices.GetRequiredService();
- app.UseAntiforgeryTokens(restBasePath);
+ app.UseAntiforgeryTokens(apiBasePath);
}
app.UseMvc(routes =>
{
@@ -430,7 +466,7 @@ public void Configure(IApplicationBuilder app, Microsoft.AspNetCore.Hosting.IHos
app.UseWebSockets();
string basePath = string.IsNullOrEmpty(VirtualPath) ? string.Empty : $"/{VirtualPath}";
- Config.ScriptPath = basePath;
+ Config.ScriptPath = string.IsNullOrEmpty(basePath) ? "/" : basePath;
app.MapWebSocketManager(basePath);
app.MapWhen(
@@ -501,7 +537,6 @@ bool IsAspx(HttpContext context, string basePath)
}
public class CustomExceptionHandlerMiddleware
{
- const string InvalidCSRFToken = "InvalidCSRFToken";
static readonly IGXLogger log = GXLoggerFactory.GetLogger();
public async Task Invoke(HttpContext httpContext)
{
@@ -516,9 +551,8 @@ public async Task Invoke(HttpContext httpContext)
}
else if (ex is AntiforgeryValidationException)
{
- //"The required antiforgery header value "X-GXCSRF-TOKEN" is not present.
httpStatusCode = HttpStatusCode.BadRequest;
- httpReasonPhrase = InvalidCSRFToken;
+ httpReasonPhrase = HttpHelper.InvalidCSRFToken;
GXLogging.Error(log, $"Validation of antiforgery failed", ex);
}
else
diff --git a/dotnet/src/dotnetcore/GxOffice/GxOffice.csproj b/dotnet/src/dotnetcore/GxOffice/GxOffice.csproj
index 3169f7bcf..094c0b13e 100644
--- a/dotnet/src/dotnetcore/GxOffice/GxOffice.csproj
+++ b/dotnet/src/dotnetcore/GxOffice/GxOffice.csproj
@@ -36,6 +36,7 @@
+
diff --git a/dotnet/src/dotnetcore/GxPdfReportsCS/GlobalSuppressions.cs b/dotnet/src/dotnetcore/GxPdfReportsCS/GlobalSuppressions.cs
index dc00e11aa..74e1befce 100644
--- a/dotnet/src/dotnetcore/GxPdfReportsCS/GlobalSuppressions.cs
+++ b/dotnet/src/dotnetcore/GxPdfReportsCS/GlobalSuppressions.cs
@@ -16,5 +16,5 @@
[assembly: SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "", Scope = "member", Target = "~M:com.genexus.reports.NativeSharpFunctionsMS.getRegistrySubValues(System.String,System.String)~System.Collections.ArrayList")]
[assembly: SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "", Scope = "member", Target = "~M:com.genexus.reports.NativeSharpFunctionsMS.ReadRegKey(System.String)~System.String")]
[assembly: SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "", Scope = "member", Target = "~M:com.genexus.reports.MSPDFFontDescriptor.getTrueTypeFontLocation(System.String)~System.String")]
-[assembly: SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "", Scope = "member", Target = "~M:com.genexus.reports.PDFReportItextBase.getAcrobatLocation~System.String")]
-[assembly: SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "", Scope = "member", Target = "~M:com.genexus.reports.PDFReportItextBase.loadSubstituteTable")]
+[assembly: SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "", Scope = "member", Target = "~M:com.genexus.reports.PDFReportBase.getAcrobatLocation~System.String")]
+[assembly: SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "", Scope = "member", Target = "~M:com.genexus.reports.PDFReportBase.loadSubstituteTable")]
diff --git a/dotnet/src/dotnetcore/GxPdfReportsCS/GxPdfReportsCS.csproj b/dotnet/src/dotnetcore/GxPdfReportsCS/GxPdfReportsCS.csproj
index 01a52310d..95fa355b4 100644
--- a/dotnet/src/dotnetcore/GxPdfReportsCS/GxPdfReportsCS.csproj
+++ b/dotnet/src/dotnetcore/GxPdfReportsCS/GxPdfReportsCS.csproj
@@ -15,8 +15,12 @@
+
+
+
+
diff --git a/dotnet/src/dotnetcore/GxPdfReportsCS/PDFReportItext8.cs b/dotnet/src/dotnetcore/GxPdfReportsCS/PDFReportItext8.cs
index bad1d5e68..c927b8281 100644
--- a/dotnet/src/dotnetcore/GxPdfReportsCS/PDFReportItext8.cs
+++ b/dotnet/src/dotnetcore/GxPdfReportsCS/PDFReportItext8.cs
@@ -35,7 +35,7 @@ public class GxReportBuilderPdf8 : GxReportBuilderPdf
public GxReportBuilderPdf8() { }
public GxReportBuilderPdf8(string appPath, Stream outputStream)
{
-
+ _appPath = appPath;
_pdfReport = new com.genexus.reports.PDFReportItext8(appPath);
if (outputStream != null)
{
@@ -49,7 +49,7 @@ public GxReportBuilderPdf8(string appPath, Stream outputStream)
namespace com.genexus.reports
{
- public class PDFReportItext8 : PDFReportItextBase
+ public class PDFReportItext8 : PDFReportBase
{
static IGXLogger log = GXLoggerFactory.GetLogger();
diff --git a/dotnet/src/dotnetcore/GxPdfReportsCS/PDFReportPDFPig.cs b/dotnet/src/dotnetcore/GxPdfReportsCS/PDFReportPDFPig.cs
new file mode 100644
index 000000000..0f1a30120
--- /dev/null
+++ b/dotnet/src/dotnetcore/GxPdfReportsCS/PDFReportPDFPig.cs
@@ -0,0 +1,1044 @@
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Drawing.Drawing2D;
+using System.Drawing.Imaging;
+using System.Drawing.Text;
+using System.IO;
+using System.Net.Http;
+using System.Text;
+using GeneXus;
+using UglyToad.PdfPig.Core;
+using UglyToad.PdfPig.Fonts.Standard14Fonts;
+using UglyToad.PdfPig.Writer;
+using static GeneXus.Utils.StringUtil;
+using static UglyToad.PdfPig.Writer.PdfDocumentBuilder;
+using static UglyToad.PdfPig.Writer.PdfPageBuilder;
+using Color = System.Drawing.Color;
+using Font = System.Drawing.Font;
+using PageSize = UglyToad.PdfPig.Content.PageSize;
+using PdfRectangle = UglyToad.PdfPig.Core.PdfRectangle;
+
+namespace GeneXus.Printer
+{
+ public class GxReportBuilderPDFPig : GxReportBuilderPdf
+ {
+ static IGXLogger log = GXLoggerFactory.GetLogger();
+ public GxReportBuilderPDFPig() { }
+ public GxReportBuilderPDFPig(string appPath, Stream outputStream)
+ {
+ _appPath = appPath;
+ _pdfReport = new com.genexus.reports.PDFReportPDFPig(appPath);
+ if (outputStream != null)
+ {
+ _pdfReport.setOutputStream(outputStream);
+ GXLogging.Debug(log, "GxReportBuilderPdf outputStream: binaryWriter");
+ }
+ }
+ }
+
+}
+
+namespace com.genexus.reports
+{
+ public class PDFReportPDFPig : PDFReportBase
+ {
+ static IGXLogger log = GXLoggerFactory.GetLogger();
+
+ private PdfDocumentBuilder documentBuilder;
+ private PdfPageBuilder pageBuilder;
+ private ExtendedPageSize pageSize;
+
+ private AddedFont baseFont;
+ private string baseFontPath;
+ private string baseFontName;
+ private Color backColor, foreColor;
+
+ private string barcodeType = null;
+
+ private Dictionary documentImages;
+
+ protected override void init(ref int gxYPage, ref int gxXPage, int pageWidth, int pageLength)
+ {
+ try
+ {
+ pageSize = ComputePageSize(leftMargin, topMargin, pageWidth, pageLength, props.getBooleanGeneralProperty(Const.MARGINS_INSIDE_BORDER, Const.DEFAULT_MARGINS_INSIDE_BORDER));
+ documentBuilder = new PdfDocumentBuilder();
+
+ pageBuilder = pageSize.IsCustomPageSize() ? documentBuilder.AddPage(pageSize.Width, pageSize.Height) : documentBuilder.AddPage(pageSize.PageSize);
+
+ gxXPage = (int) pageBuilder.PageSize.TopRight.X;
+ if (props.getBooleanGeneralProperty(Const.FIX_SAC24437, true))
+ gxYPage = (int)(pageLength / GX_PAGE_SCALE_Y);
+ else
+ gxYPage = (int)(pageLength / GX_PAGE_SCALE_Y_OLD);
+
+ }
+ catch (Exception e)
+ {
+ GXLogging.Error(log, "GxPrintInit failed", e);
+ }
+ }
+
+ public PDFReportPDFPig(string appPath) : base(appPath)
+ {
+ documentBuilder = null;
+ documentImages = new Dictionary();
+ }
+
+ public override void GxStartPage()
+ {
+ try
+ {
+ if (pages > 0)
+ {
+ pageBuilder = pageSize.IsCustomPageSize() ? documentBuilder.AddPage(pageSize.Width, pageSize.Height) : documentBuilder.AddPage(pageSize.PageSize);
+ }
+ pages = pages + 1;
+ }
+ catch (Exception de)
+ {
+ GXLogging.Error(log, "GxStartPage error", de);
+ }
+ }
+
+ internal override bool SetComplainceLevel(PdfConformanceLevel level)
+ {
+ return false;
+ }
+
+ public override void GxDrawRect(int left, int top, int right, int bottom, int pen, int foreRed, int foreGreen, int foreBlue, int backMode, int backRed, int backGreen, int backBlue,
+ int styleTop, int styleBottom, int styleRight, int styleLeft, int cornerRadioTL, int cornerRadioTR, int cornerRadioBL, int cornerRadioBR)
+ {
+ float penAux = (float)convertScale(pen);
+ float rightAux = (float)convertScale(right);
+ float bottomAux = (float)convertScale(bottom);
+ float leftAux = (float)convertScale(left);
+ float topAux = (float)convertScale(top);
+
+ float x1, y1, x2, y2;
+ x1 = leftAux + leftMargin;
+ y1 = (float)pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin;
+ x2 = rightAux + leftMargin;
+ y2 = (float)pageBuilder.PageSize.TopRight.Y - topAux - topMargin - bottomMargin;
+
+ GXLogging.Info(log, "Corner styling and radio are not taken into consideration because the PDFPig " +
+ "API provides no way to render rounded rectangles or style them");
+
+ if (pen > 0)
+ pageBuilder.SetStrokeColor( (byte)foreRed, (byte)foreGreen, (byte)foreBlue);
+ else
+ pageBuilder.SetStrokeColor( (byte)backRed, (byte)backGreen, (byte)backBlue);
+
+ GXLogging.Info(log, "The PDFPig API provides no way of setting the line cap style");
+
+ if (backMode != 0)
+ {
+ pageBuilder.SetTextAndFillColor((byte)backRed, (byte)backGreen, (byte)backBlue);
+ pageBuilder.DrawRectangle(new PdfPoint(x1, y1), (decimal)(x2 - x1), (decimal)(y2 - y1), (decimal)penAux, true);
+ }
+ else
+ {
+ pageBuilder.DrawRectangle(new PdfPoint(x1, y1), (decimal)(x2 - x1), (decimal)(y2 - y1), (decimal) penAux, false);
+ }
+ pageBuilder.ResetColor();
+ }
+
+ public override void GxDrawLine(int left, int top, int right, int bottom, int width, int foreRed, int foreGreen, int foreBlue, int style)
+ {
+ try
+ {
+ float widthAux = (float)convertScale(width);
+ float rightAux = (float)convertScale(right);
+ float bottomAux = (float)convertScale(bottom);
+ float leftAux = (float)convertScale(left);
+ float topAux = (float)convertScale(top);
+
+ GXLogging.Debug(log, "GxDrawLine -> (" + left + "," + top + ") - (" + right + "," + bottom + ") Width: " + width);
+
+ float x1, y1, x2, y2;
+
+ x1 = leftAux + leftMargin;
+ y1 = (float)pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin;
+ x2 = rightAux + leftMargin;
+ y2 = (float)pageBuilder.PageSize.TopRight.Y - topAux - topMargin - bottomMargin;
+
+ pageBuilder.SetStrokeColor((byte)foreRed, (byte)foreGreen, (byte)foreBlue);
+
+ if (style != 0)
+ {
+ GXLogging.Info(log, "The PDFPig API provides no way of creating a dashed line");
+ float[] dashPattern = getDashedPattern(style);
+ }
+
+ pageBuilder.DrawLine(new PdfPoint(x1, y1), new PdfPoint(x2, y2), (decimal)widthAux);
+ pageBuilder.ResetColor();
+ }
+ catch (Exception e)
+ {
+ GXLogging.Error(log, "GxDrawLine error", e);
+ }
+ }
+
+ public override void GxDrawBitMap(string bitmap, int left, int top, int right, int bottom, int aspectRatio)
+ {
+ try
+ {
+ string imageType = Path.GetExtension(bitmap).Substring(1);
+
+ float rightAux = (float)convertScale(right);
+ float bottomAux = (float)convertScale(bottom);
+ float leftAux = (float)convertScale(left);
+ float topAux = (float)convertScale(top);
+
+ float llx = leftAux + leftMargin;
+ float lly = (float) pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin;
+ float width;
+ float height;
+ if (aspectRatio == 0)
+ {
+ width = rightAux - leftAux;
+ height = bottomAux - topAux;
+ }
+ else
+ {
+ width = (rightAux - leftAux) * aspectRatio;
+ height = (bottomAux - topAux) * aspectRatio;
+ }
+
+ PdfRectangle position = new PdfRectangle(llx, lly, llx + width, lly + height);
+
+ AddedImage image = null;
+ AddedImage imageRef;
+ if (documentImages != null && documentImages.TryGetValue(bitmap, out imageRef))
+ {
+ image = imageRef;
+ }
+ else
+ {
+ try
+ {
+ if (!Path.IsPathRooted(bitmap))
+ {
+
+ image = imageType == "jpeg" ? pageBuilder.AddJpeg(File.ReadAllBytes(defaultRelativePrepend + bitmap), position) : pageBuilder.AddPng(File.ReadAllBytes(defaultRelativePrepend + bitmap), position);
+ if (image == null)
+ {
+ bitmap = webAppDir + bitmap;
+ image = imageType == "jpeg" ? pageBuilder.AddJpeg(File.ReadAllBytes(bitmap), position) : pageBuilder.AddPng(File.ReadAllBytes(bitmap), position);
+ }
+ else
+ {
+ bitmap = defaultRelativePrepend + bitmap;
+ }
+ }
+ else
+ {
+ image = imageType == "jpeg" ? pageBuilder.AddJpeg(File.ReadAllBytes(bitmap), position) : pageBuilder.AddPng(File.ReadAllBytes(bitmap), position);
+ }
+ }
+ catch (Exception)
+ {
+ image = AddImageFromURL(bitmap, position);
+ }
+ if (image == null)
+ {
+ image = AddImageFromURL(bitmap, position);
+ }
+
+ if (documentImages == null)
+ {
+ documentImages = new Dictionary();
+ }
+ documentImages[bitmap] = image;
+ }
+ GXLogging.Debug(log, "GxDrawBitMap -> '" + bitmap + "' [" + left + "," + top + "] - Size: (" + (right - left) + "," + (bottom - top) + ")");
+ }
+ catch (Exception e)
+ {
+ GXLogging.Error(log, "GxDrawBitMap error", e);
+ }
+ }
+
+ private AddedImage AddImageFromURL(string url, PdfRectangle position)
+ {
+ AddedImage image = null;
+ using (HttpClient httpClient = new HttpClient())
+ {
+ byte[] imageBytes = httpClient.GetByteArrayAsync(url).Result;
+ try
+ {
+ image = pageBuilder.AddJpeg(imageBytes, position);
+ }
+ catch (Exception)
+ {
+ pageBuilder.AddPng(imageBytes, position);
+ }
+ }
+ if (image == null)
+ {
+ GXLogging.Error(log, "GxDrawBitMap : PDFPig only supports adding jpeg or png images to documents");
+ }
+ return image;
+ }
+
+ public override void GxAttris(string fontName, int fontSize, bool fontBold, bool fontItalic, bool fontUnderline, bool fontStrikethru, int pen, int foreRed, int foreGreen, int foreBlue, int backMode, int backRed, int backGreen, int backBlue)
+ {
+ bool isCJK = false;
+ bool embedFont = IsEmbeddedFont(fontName);
+ string originalFontName = fontName;
+ if (!embedFont)
+ {
+ fontName = getSubstitute(fontName);
+ }
+
+ string fontSubstitute = originalFontName != fontName ? $"Original Font: {originalFontName} Substitute" : "";
+
+ GXLogging.Debug(log, $"GxAttris: ");
+ GXLogging.Debug(log, $"\\-> Font: {fontName} ({fontSize})" +
+ (fontBold ? " BOLD" : "") +
+ (fontItalic ? " ITALIC" : "") +
+ (fontStrikethru ? " Strike" : ""));
+ GXLogging.Debug(log, $"\\-> Fore ({foreRed}, {foreGreen}, {foreBlue})");
+ GXLogging.Debug(log, $"\\-> Back ({backRed}, {backGreen}, {backBlue})");
+
+ this.fontUnderline = fontUnderline;
+ this.fontStrikethru = fontStrikethru;
+ this.fontSize = fontSize;
+ foreColor = Color.FromArgb(foreRed, foreGreen, foreBlue);
+ backColor = Color.FromArgb(backRed, backGreen, backBlue);
+
+ backFill = backMode != 0;
+
+ try
+ {
+ string fontNameLower = fontName.ToLower();
+ if (PDFFont.isType1(fontName))
+ {
+ foreach (string[] cjkName in Type1FontMetrics.CJKNames)
+ {
+ if (cjkName[0].ToLower().Equals(fontNameLower) || cjkName[1].ToLower().Equals(fontNameLower))
+ {
+ string style = fontBold && fontItalic ? "BoldItalic" : fontItalic ? "Italic" : fontBold ? "Bold" : "";
+ setAsianFont(fontName, style);
+ isCJK = true;
+ break;
+ }
+ }
+ if (!isCJK)
+ {
+ int style = (fontBold && fontItalic ? 3 : 0) + (fontItalic && !fontBold ? 2 : 0) + (fontBold && !fontItalic ? 1 : 0);
+ foreach (string[] base14Font in PDFFont.base14)
+ {
+ if (base14Font[0].ToLower().Equals(fontNameLower))
+ {
+ fontName = base14Font[1 + style].Substring(1);
+ break;
+ }
+ }
+ ProcessBaseFont(fontName);
+ }
+ }
+ else
+ {
+ string style = (fontBold && fontItalic ? ",BoldItalic" : "") + (fontItalic && !fontBold ? ",Italic" : "") + (fontBold && !fontItalic ? ",Bold" : "");
+ fontName += style;
+ ProcessBaseFont(fontName);
+ }
+
+ if (barcode128AsImage && (
+ fontName.ToLower().Contains("barcode 128") || fontName.ToLower().Contains("barcode128")
+ ||
+ (!string.IsNullOrEmpty(baseFontPath) && (baseFontPath.ToLower().Contains("3of9") || baseFontPath.ToLower().Contains("3 of 9")))
+ )
+ )
+ {
+ barcodeType = "barcode128";
+ }
+ }
+ catch (Exception e)
+ {
+ GXLogging.Debug(log, "GxAttris DocumentException", e);
+ throw;
+ }
+ }
+
+ private void ProcessBaseFont(string fontName)
+ {
+ string fontLocation = GetFontLocation(fontName);
+ if (string.IsNullOrEmpty(fontLocation))
+ {
+ fontLocation = new MSPDFFontDescriptor().getTrueTypeFontLocation(fontName);
+ }
+
+ if (!string.IsNullOrEmpty(fontLocation))
+ {
+ byte[] fontBytes = File.ReadAllBytes(fontLocation);
+ baseFont = documentBuilder.AddTrueTypeFont(fontBytes);
+ }
+ else
+ {
+ baseFont = createType1FontFromName(fontName);
+ if (baseFont == null)
+ baseFont = documentBuilder.AddStandard14Font(Standard14Font.TimesRoman);
+ }
+
+ baseFontName = fontName;
+ baseFontPath = fontLocation;
+ }
+
+ private AddedFont createType1FontFromName(string fontName)
+ {
+ switch (fontName.ToLower())
+ {
+ case "times-roman":
+ return documentBuilder.AddStandard14Font(Standard14Font.TimesRoman);
+ case "times-bold":
+ return documentBuilder.AddStandard14Font(Standard14Font.TimesBold);
+ case "times-italic":
+ return documentBuilder.AddStandard14Font(Standard14Font.TimesItalic);
+ case "times-bolditalic":
+ return documentBuilder.AddStandard14Font(Standard14Font.TimesBoldItalic);
+ case "helvetica":
+ return documentBuilder.AddStandard14Font(Standard14Font.Helvetica);
+ case "helvetica-bold":
+ return documentBuilder.AddStandard14Font(Standard14Font.HelveticaBold);
+ case "helvetica-oblique":
+ return documentBuilder.AddStandard14Font(Standard14Font.HelveticaOblique);
+ case "helvetica-boldoblique":
+ return documentBuilder.AddStandard14Font(Standard14Font.HelveticaBoldOblique);
+ case "courier":
+ return documentBuilder.AddStandard14Font(Standard14Font.Courier);
+ case "courier-bold":
+ return documentBuilder.AddStandard14Font(Standard14Font.CourierBold);
+ case "courier-oblique":
+ return documentBuilder.AddStandard14Font(Standard14Font.CourierOblique);
+ case "courier-boldoblique":
+ return documentBuilder.AddStandard14Font(Standard14Font.CourierBoldOblique);
+ case "symbol":
+ return documentBuilder.AddStandard14Font(Standard14Font.Symbol);
+ case "zapfdingbats":
+ return documentBuilder.AddStandard14Font(Standard14Font.ZapfDingbats);
+ default:
+ return null;
+ }
+ }
+
+ public override void setAsianFont(string fontName, string style)
+ {
+ try
+ {
+ string fontPath = GetFontLocation(fontName);
+ if (string.IsNullOrEmpty(fontPath))
+ {
+ MSPDFFontDescriptor fontDescriptor = new MSPDFFontDescriptor();
+ fontPath = fontDescriptor.getTrueTypeFontLocation(fontName);
+ }
+ byte[] fontBytes = File.ReadAllBytes(fontPath);
+ baseFont = documentBuilder.AddTrueTypeFont(fontBytes);
+ }
+ catch (IOException de)
+ {
+ GXLogging.Debug(log, "setAsianFont error", de);
+ }
+ }
+
+ public override void GxDrawText(string sTxt, int left, int top, int right, int bottom, int align, int htmlformat, int border, int valign)
+ {
+ bool printRectangle = false;
+ if (props.getBooleanGeneralProperty(Const.BACK_FILL_IN_CONTROLS, true))
+ printRectangle = true;
+
+ if (printRectangle && (border == 1 || backFill))
+ {
+ GxDrawRect(left, top, right, bottom, border, foreColor.R, foreColor.G, foreColor.B, backFill ? 1 : 0, backColor.R, backColor.G, backColor.B, 0, 0);
+ }
+
+ sTxt = RTrim(sTxt);
+
+ AddedFont font = createType1FontFromName(baseFontName);
+ if (font == null)
+ {
+ try
+ {
+ byte[] fontBytes = File.ReadAllBytes(baseFontPath);
+ font = documentBuilder.AddTrueTypeFont(fontBytes);
+ }
+ catch
+ {
+ font = baseFont;
+ }
+ }
+
+ float captionHeight = CalculateFontCaptionHeight(baseFontPath, fontSize);
+ float rectangleWidth = MeasureTextWidth(sTxt, baseFontPath, fontSize);
+ float lineHeight = MeasureTextHeight(sTxt, baseFontPath, fontSize);
+ float textBlockHeight = (float)convertScale(bottom - top);
+ int linesCount = (int)(textBlockHeight / lineHeight);
+ int bottomOri = bottom;
+ int topOri = top;
+
+ if (linesCount >= 2 && !((align & 16) == 16) && htmlformat != 1)
+ {
+ if (valign == (int)VerticalAlign.TOP)
+ bottom = top + (int)reconvertScale(lineHeight);
+ else if (valign == (int)VerticalAlign.BOTTOM)
+ top = bottom - (int)reconvertScale(lineHeight);
+ }
+
+ float bottomAux = (float)convertScale(bottom) - ((float)convertScale(bottom - top)) / 2;
+ float topAux = (float)convertScale(top) + ((float)convertScale(bottom - top)) / 2;
+
+ float startHeight = bottomAux - topAux - captionHeight;
+
+ float leftAux = (float)convertScale(left);
+ float rightAux = (float)convertScale(right);
+ int alignment = align & 3;
+ bool autoResize = (align & 256) == 256;
+
+ if (htmlformat == 1)
+ {
+ GXLogging.Error(log, "GxDrawText: PDFPig report implementation does not support rendering HTML content into PDF reports");
+ }
+
+ if (barcodeType != null)
+ {
+ PdfRectangle barcodeRectangle;
+ switch (alignment)
+ {
+ case 1: // Center Alignment
+ barcodeRectangle = new PdfRectangle(
+ (leftAux + rightAux) / 2 + leftMargin - rectangleWidth / 2,
+ pageBuilder.PageSize.TopRight.Y - (float)convertScale(bottom) - topMargin - bottomMargin,
+ (leftAux + rightAux) / 2 + leftMargin + rectangleWidth / 2,
+ pageBuilder.PageSize.TopRight.Y - (float)convertScale(top) - topMargin - bottomMargin
+ );
+ break;
+ case 2: // Right Alignment
+ barcodeRectangle = new PdfRectangle(
+ rightAux + leftMargin - rectangleWidth,
+ pageBuilder.PageSize.TopRight.Y - (float)convertScale(bottom) - topMargin - bottomMargin,
+ rightAux + leftMargin,
+ pageBuilder.PageSize.TopRight.Y - (float)convertScale(top) - topMargin - bottomMargin
+ );
+ break;
+ default: // Left Alignment (Corresponds to alignment = 0 but used as default)
+ barcodeRectangle = new PdfRectangle(
+ leftAux + leftMargin,
+ pageBuilder.PageSize.TopRight.Y - (float)convertScale(bottom) - topMargin - bottomMargin,
+ leftAux + leftMargin + rectangleWidth,
+ pageBuilder.PageSize.TopRight.Y - (float)convertScale(top) - topMargin - bottomMargin
+ );
+ break;
+
+ }
+ Image barcodeImage = CreateBarcodeImage((float)barcodeRectangle.Width, (float)barcodeRectangle.Height, baseFontPath, sTxt);
+ using (MemoryStream ms = new MemoryStream())
+ {
+ barcodeImage.Save(ms, ImageFormat.Png);
+ pageBuilder.AddPng(ms.ToArray(), barcodeRectangle);
+ }
+ return;
+ }
+
+ if (backFill)
+ {
+ PdfPoint rectangleStartingPoint;
+ switch (alignment)
+ {
+ case 1: // Center Alignment
+ rectangleStartingPoint = new PdfPoint(
+ (leftAux + rightAux) / 2 + leftMargin - rectangleWidth / 2,
+ pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin
+ );
+ break;
+ case 2: // Right Alignment
+ rectangleStartingPoint = new PdfPoint(
+ rightAux + leftMargin - rectangleWidth,
+ pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin
+ );
+ break;
+ default: // Left Alignment (Corresponds to alignment = 0 but used as default)
+ rectangleStartingPoint = new PdfPoint(
+ leftAux + leftMargin,
+ pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin
+ );
+ break;
+
+ }
+
+ decimal width = (decimal)(((leftAux + rightAux) / 2 + leftMargin + rectangleWidth / 2) - ((leftAux + rightAux) / 2 + leftMargin - rectangleWidth / 2));
+ decimal height = (decimal)((pageBuilder.PageSize.TopRight.Y - topAux - topMargin - bottomMargin) - (pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin));
+
+ pageBuilder.SetTextAndFillColor(backColor.R, backColor.G, backColor.B);
+ pageBuilder.DrawRectangle(rectangleStartingPoint, width, height, 1, true);
+ }
+
+ pageBuilder.SetTextAndFillColor(foreColor.R, foreColor.G, foreColor.B);
+
+ if (fontUnderline)
+ {
+ float underlineSeparation = lineHeight / 5;
+ int underlineHeight = (int)underlineSeparation + (int)(underlineSeparation / 4);
+
+ PdfPoint underlineStartingPoint;
+ PdfPoint underlineEndingPoint;
+ switch (alignment)
+ {
+ case 1: // Center Alignment
+ underlineStartingPoint = new PdfPoint(
+ (leftAux + rightAux) / 2 + leftMargin - rectangleWidth / 2,
+ pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin + startHeight
+ );
+ underlineEndingPoint = new PdfPoint(
+ (leftAux + rightAux) / 2 + leftMargin + rectangleWidth / 2,
+ pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin + startHeight
+ );
+ break;
+ case 2: // Right Alignment
+ underlineStartingPoint = new PdfPoint(
+ rightAux + leftMargin - rectangleWidth,
+ pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin + startHeight
+ );
+ underlineEndingPoint = new PdfPoint(
+ rightAux + leftMargin,
+ pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin + startHeight
+ );
+ break;
+ default: // Left Alignment (Corresponds to alignment = 0 but used as default)
+ underlineStartingPoint = new PdfPoint(
+ leftAux + leftMargin,
+ pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin + startHeight
+ );
+ underlineEndingPoint = new PdfPoint(
+ leftAux + leftMargin + rectangleWidth,
+ pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin + startHeight
+ );
+ break;
+
+ }
+
+ pageBuilder.DrawLine(underlineStartingPoint, underlineEndingPoint, underlineHeight);
+ }
+
+ if (fontStrikethru)
+ {
+ float strikethruSeparation = (float)(lineHeight / 1.5);
+ PdfPoint strikethruStartingPoint;
+ PdfPoint strikethruEndingPoint;
+ switch (alignment)
+ {
+ case 1: // Center Alignment
+ strikethruStartingPoint = new PdfPoint(
+ (leftAux + rightAux) / 2 + leftMargin - rectangleWidth / 2,
+ pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin + startHeight + strikethruSeparation
+ );
+ strikethruEndingPoint = new PdfPoint(
+ (leftAux + rightAux) / 2 + leftMargin + rectangleWidth / 2,
+ pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin + startHeight + strikethruSeparation
+ );
+ break;
+ case 2: // Right Alignment
+ strikethruStartingPoint = new PdfPoint(
+ rightAux + leftMargin - rectangleWidth,
+ pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin + startHeight + strikethruSeparation
+ );
+ strikethruEndingPoint = new PdfPoint(
+ rightAux + leftMargin,
+ pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin + startHeight + strikethruSeparation
+ );
+ break;
+ default: // Left Alignment (Corresponds to alignment = 0 but used as default)
+ strikethruStartingPoint = new PdfPoint(
+ leftAux + leftMargin,
+ pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin + startHeight + strikethruSeparation
+ );
+ strikethruEndingPoint = new PdfPoint(
+ leftAux + leftMargin + rectangleWidth,
+ pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin + startHeight + strikethruSeparation
+ );
+ break;
+
+ }
+ pageBuilder.DrawLine(strikethruStartingPoint, strikethruEndingPoint, (decimal)convertScale(fontSize) / 10);
+ }
+
+ float textBlockWidth = rightAux - leftAux;
+ float TxtWidth = MeasureTextWidth(sTxt, baseFontPath, fontSize);
+ bool justified = (alignment == 3) && textBlockWidth < TxtWidth;
+ bool wrap = ((align & 16) == 16);
+
+ if (wrap || justified)
+ {
+ bottomAux = (float)convertScale(bottomOri);
+ topAux = (float)convertScale(topOri);
+
+ float llx = leftAux + leftMargin;
+ float lly = (float)(pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin);
+ float urx = rightAux + leftMargin;
+ float ury = (float)(pageBuilder.PageSize.TopRight.Y - topAux - topMargin - bottomMargin);
+
+ ShowWrappedTextAligned(font, alignment, sTxt, llx, lly, urx, ury);
+ }
+ else
+ {
+ if (!autoResize)
+ {
+ string newsTxt = sTxt;
+ while (TxtWidth > textBlockWidth && (newsTxt.Length - 1 >= 0))
+ {
+ sTxt = newsTxt;
+ newsTxt = newsTxt.Substring(0, newsTxt.Length - 1);
+ TxtWidth = MeasureTextWidth(sTxt, baseFontPath, fontSize);
+ }
+ }
+ switch (alignment)
+ {
+ case 1: // Center Alignment
+ ShowTextAligned(font, alignment, sTxt, ((leftAux + rightAux) / 2) + leftMargin, (float)(pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin));
+ break;
+ case 2: // Right Alignment
+ ShowTextAligned(font, alignment, sTxt, rightAux + leftMargin, (float)(pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin));
+ break;
+ case 0: // Left Alignment
+ case 3: // Justified, only one text line
+ ShowTextAligned(font, alignment, sTxt, leftAux + leftMargin, (float)(pageBuilder.PageSize.TopRight.Y - bottomAux - topMargin - bottomMargin));
+ break;
+ }
+ }
+ }
+
+ public Image CreateBarcodeImage(float width, float height, string fontPath, string text)
+ {
+ PrivateFontCollection fontCollection = new PrivateFontCollection();
+ fontCollection.AddFontFile(fontPath);
+ FontFamily fontFamily = fontCollection.Families[0];
+
+ int bitmapWidth = (int)Math.Ceiling(width);
+ int bitmapHeight = (int)Math.Ceiling(height);
+
+ float fontSize = Math.Min(width, height);
+ Font font;
+ SizeF textSize;
+
+ using (Bitmap tempBitmap = new Bitmap(1, 1))
+ {
+ using (Graphics tempGraphics = Graphics.FromImage(tempBitmap))
+ {
+ do
+ {
+ font = new Font(fontFamily, fontSize, GraphicsUnit.Pixel);
+ textSize = tempGraphics.MeasureString(text, font);
+ fontSize--;
+ } while (textSize.Width > width || textSize.Height > height);
+ }
+ }
+
+ Bitmap bitmap = new Bitmap(bitmapWidth, bitmapHeight);
+ bitmap.SetResolution(600, 600);
+ using (Graphics graphics = Graphics.FromImage(bitmap))
+ {
+ graphics.Clear(Color.White);
+ graphics.SmoothingMode = SmoothingMode.HighQuality;
+ graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
+ graphics.TextRenderingHint = TextRenderingHint.SingleBitPerPixelGridFit;
+ StringFormat format = new StringFormat
+ {
+ Alignment = StringAlignment.Center,
+ LineAlignment = StringAlignment.Center
+ };
+ graphics.DrawString(text, font, Brushes.Black, new RectangleF(0, 0, width, height), format);
+ }
+ font.Dispose();
+ fontCollection.Dispose();
+
+ return bitmap;
+ }
+
+ private float MeasureTextWidth(string text, string fontPath, float fontSize)
+ {
+ Font font;
+ if (string.IsNullOrEmpty(fontPath))
+ {
+ font = new Font("Times New Roman", fontSize, GraphicsUnit.Point);
+ }
+ else
+ {
+ PrivateFontCollection pfc = new PrivateFontCollection();
+ pfc.AddFontFile(fontPath);
+ font = new Font(pfc.Families[0], fontSize, GraphicsUnit.Point);
+ }
+
+ using (font)
+ {
+ using (var fakeImage = new Bitmap(1, 1))
+ {
+ using (var graphics = Graphics.FromImage(fakeImage))
+ {
+ graphics.PageUnit = GraphicsUnit.Point;
+ graphics.TextRenderingHint = TextRenderingHint.AntiAlias;
+ var sizeF = graphics.MeasureString(text, font);
+ return sizeF.Width;
+ }
+ }
+ }
+ }
+
+ private float MeasureTextHeight(string text, string fontPath, float fontSize)
+ {
+ Font font;
+ if (string.IsNullOrEmpty(fontPath))
+ {
+ font = new Font("Times New Roman", fontSize, GraphicsUnit.Point);
+ }
+ else
+ {
+ PrivateFontCollection pfc = new PrivateFontCollection();
+ pfc.AddFontFile(fontPath);
+ font = new Font(pfc.Families[0], fontSize, GraphicsUnit.Point);
+ }
+
+ using (font)
+ {
+ using (var fakeImage = new Bitmap(1, 1))
+ {
+ using (var graphics = Graphics.FromImage(fakeImage))
+ {
+ graphics.PageUnit = GraphicsUnit.Point;
+ graphics.TextRenderingHint = TextRenderingHint.AntiAlias;
+ var sizeF = graphics.MeasureString(text, font);
+ return sizeF.Height;
+ }
+ }
+ }
+ }
+
+ private float CalculateFontCaptionHeight(string fontPath, float fontSize, FontStyle fontStyle = FontStyle.Regular)
+ {
+ Font font;
+ if (string.IsNullOrEmpty(fontPath))
+ {
+ font = new Font("Times New Roman", fontSize, GraphicsUnit.Point);
+ }
+ else
+ {
+ PrivateFontCollection pfc = new PrivateFontCollection();
+ pfc.AddFontFile(fontPath);
+ font = new Font(pfc.Families[0], fontSize, GraphicsUnit.Point);
+ }
+ using (font)
+ {
+ FontFamily ff = font.FontFamily;
+
+ float ascent = ff.GetCellAscent(fontStyle);
+ float descent = ff.GetCellDescent(fontStyle);
+ float lineSpacing = ff.GetLineSpacing(fontStyle);
+
+ float height = fontSize * (ascent + descent) / ff.GetEmHeight(fontStyle);
+
+ return height;
+ }
+ }
+
+ private void ShowTextAligned(AddedFont font, int alignment, string text, float x, float y)
+ {
+ try
+ {
+ float textWidth = MeasureTextWidth(text, baseFontPath, fontSize);
+ switch (alignment)
+ {
+ case 0: // Left-aligned
+ case 3: // Justified, only one text line
+ break;
+ case 1: // Center-aligned
+ x = x - textWidth / 2;
+ break;
+ case 2: // Right-aligned
+ x = x - textWidth;
+ break;
+ }
+ y = (float)(y - fontSize * 0.5);
+ pageBuilder.AddText(text, fontSize, new PdfPoint(x, y), font);
+ }
+ catch (IOException ioe)
+ {
+ GXLogging.Error(log, "failed to draw aligned text: ", ioe);
+ }
+ }
+
+ private void ShowWrappedTextAligned(AddedFont font, int alignment, string text, float llx, float lly, float urx, float ury)
+ {
+ try
+ {
+ List lines = new List();
+ string[] words = text.Split(' ');
+ StringBuilder currentLine = new StringBuilder();
+ foreach (string word in words)
+ {
+ float currentLineWidth = MeasureTextWidth(currentLine + " " + word, baseFontPath, fontSize);
+ if (currentLineWidth < urx - llx)
+ {
+ if (currentLine.Length > 0)
+ {
+ currentLine.Append(" ");
+ }
+ currentLine.Append(word);
+ }
+ else
+ {
+ lines.Add(currentLine.ToString());
+ currentLine.Clear();
+ currentLine.Append(word);
+ }
+ }
+ lines.Add(currentLine.ToString());
+
+ float leading = (float)(lines.Count == 1 ? fontSize : 1.2 * fontSize);
+ float totalTextHeight = fontSize * lines.Count + leading * (lines.Count - 1);
+ float startY = lines.Count == 1 ? lly + (ury - lly - totalTextHeight) / 2 : lly + (ury - lly - totalTextHeight) / 2 + (lines.Count - 1) * (fontSize + leading);
+
+ foreach (string line in lines)
+ {
+ float lineWidth = MeasureTextWidth(line, baseFontPath, fontSize);
+ float startX;
+
+ switch (alignment)
+ {
+ case 1: // Center-aligned
+ startX = llx + (urx - llx - lineWidth) / 2;
+ break;
+ case 2: // Right-aligned
+ startX = urx - lineWidth;
+ break;
+ default: // Left-aligned & Justified, only one text line
+ startX = llx;
+ break;
+ }
+
+ pageBuilder.AddText(line, fontSize, new PdfPoint(startX, startY), font);
+ startY -= leading;
+ }
+ }
+ catch (IOException ioe)
+ {
+ GXLogging.Error(log, "Failed to draw wrapped text", ioe);
+ }
+ }
+
+ private ExtendedPageSize ComputePageSize(float leftMargin, float topMargin, int width, int length, bool marginsInsideBorder)
+ {
+ if ((leftMargin == 0 && topMargin == 0) || marginsInsideBorder)
+ {
+ if (length == 23818 && width == 16834)
+ return new ExtendedPageSize(PageSize.A3);
+ else if (length == 16834 && width == 11909)
+ return new ExtendedPageSize(PageSize.A4);
+ else if (length == 11909 && width == 8395)
+ return new ExtendedPageSize(PageSize.A5);
+ else if (length == 15120 && width == 10440)
+ return new ExtendedPageSize(PageSize.Executive);
+ else if (length == 20160 && width == 12240)
+ return new ExtendedPageSize(PageSize.Legal);
+ else if (length == 15840 && width == 12240)
+ return new ExtendedPageSize(PageSize.Letter);
+ else
+ return new ExtendedPageSize((width / PAGE_SCALE_X), (length / PAGE_SCALE_Y));
+ }
+ return new ExtendedPageSize((width / PAGE_SCALE_X) + leftMargin, (length / PAGE_SCALE_Y) + topMargin);
+
+ }
+
+ public override void GxEndDocument()
+ {
+ if (pages == 0)
+ {
+ GxStartPage();
+ }
+
+ GXLogging.Info(log, "The PDFPig API provides no way of setting the number of copies and the duplex value for the viewer preferences");
+
+ GXLogging.Info(log, "The PDFPig API provides no way of embedding javascript into the document");
+
+ try
+ {
+ byte[] documentBytes = documentBuilder.Build();
+ outputStream.Write(documentBytes, 0, documentBytes.Length);
+ }
+ catch (IOException e)
+ {
+ GXLogging.Debug(log,"GxEndDocument: failed to write document to the output stream", e);
+ }
+
+ string serverPrinting = props.getGeneralProperty(Const.SERVER_PRINTING);
+ bool printingScript = (outputType == Const.OUTPUT_PRINTER || outputType == Const.OUTPUT_STREAM_PRINTER) && serverPrinting.Equals("false");
+ switch (outputType)
+ {
+ case Const.OUTPUT_SCREEN:
+ try
+ {
+ outputStream.Close();
+ GXLogging.Debug(log, "GxEndDocument OUTPUT_SCREEN outputstream length" + outputStream.ToString().Length);
+ }
+ catch (IOException e)
+ {
+ GXLogging.Error(log, "GxEndDocument OUTPUT_SCREEN error", e);
+ }
+ try { showReport(docName, modal); }
+ catch (Exception) { }
+ break;
+
+ case Const.OUTPUT_PRINTER:
+ try { outputStream.Close(); }
+ catch (IOException) { }
+ try
+ {
+ if (!serverPrinting.Equals("false") && !printingScript)
+ {
+ printReport(docName, this.printerOutputMode == 0, printerSettings.getProperty(form, Const.PRINTER));
+ }
+ }
+ catch (Exception) { }
+ break;
+
+ case Const.OUTPUT_FILE:
+ try
+ {
+ outputStream.Close();
+ GXLogging.Debug(log, "GxEndDocument OUTPUT_FILE outputstream length" + outputStream.ToString().Length);
+ }
+ catch (IOException e)
+ {
+ GXLogging.Error(log, "GxEndDocument OUTPUT_FILE error", e);
+ }
+ break;
+
+ case Const.OUTPUT_STREAM:
+ case Const.OUTPUT_STREAM_PRINTER:
+ default: break;
+ }
+ outputStream = null;
+ }
+
+ }
+
+ internal class ExtendedPageSize
+ {
+ internal double Width;
+ internal double Height;
+ internal PageSize PageSize;
+ internal ExtendedPageSize(double w, double h)
+ {
+ Width = w;
+ Height = h;
+ PageSize = PageSize.Custom;
+ }
+ internal ExtendedPageSize(PageSize pageSize)
+ {
+ PageSize = pageSize;
+ }
+ internal bool IsCustomPageSize()
+ {
+ return PageSize == PageSize.Custom;
+ }
+ }
+}
diff --git a/dotnet/src/dotnetcore/GxSearch/GxSearch.csproj b/dotnet/src/dotnetcore/GxSearch/GxSearch.csproj
index e2ef170a8..8588e6c64 100644
--- a/dotnet/src/dotnetcore/GxSearch/GxSearch.csproj
+++ b/dotnet/src/dotnetcore/GxSearch/GxSearch.csproj
@@ -20,25 +20,13 @@
+
-
+
-
-
-
- False
- ..\libs\net8.0\Jayrock.dll
-
-
- False
- ..\libs\Jayrock.dll
-
-
-
-
\ No newline at end of file
diff --git a/dotnet/src/dotnetcore/Projects/StoreManager/StoreManager.csproj b/dotnet/src/dotnetcore/Projects/StoreManager/StoreManager.csproj
index 64129d3dd..b4815e353 100644
--- a/dotnet/src/dotnetcore/Projects/StoreManager/StoreManager.csproj
+++ b/dotnet/src/dotnetcore/Projects/StoreManager/StoreManager.csproj
@@ -6,7 +6,9 @@
GeneXus.SD.Store
StoreManager
GeneXus.StoreManager.Core
-
+ NETCORE
+
+
@@ -25,8 +27,9 @@
-
+
+
@@ -36,15 +39,4 @@
-
-
- False
- ..\..\libs\net8.0\Jayrock.dll
-
-
- False
- ..\..\libs\Jayrock.dll
-
-
-
diff --git a/dotnet/src/dotnetcore/Providers/Cache/GxMemcached/GxMemcached.csproj b/dotnet/src/dotnetcore/Providers/Cache/GxMemcached/GxMemcached.csproj
index f69db1bbf..ecc9fba40 100644
--- a/dotnet/src/dotnetcore/Providers/Cache/GxMemcached/GxMemcached.csproj
+++ b/dotnet/src/dotnetcore/Providers/Cache/GxMemcached/GxMemcached.csproj
@@ -15,7 +15,8 @@
-
+
+
diff --git a/dotnet/src/dotnetcore/Providers/Cache/GxRedis/GxRedis.csproj b/dotnet/src/dotnetcore/Providers/Cache/GxRedis/GxRedis.csproj
index 9d38c6b46..f616027d3 100644
--- a/dotnet/src/dotnetcore/Providers/Cache/GxRedis/GxRedis.csproj
+++ b/dotnet/src/dotnetcore/Providers/Cache/GxRedis/GxRedis.csproj
@@ -18,10 +18,14 @@
-
+
+
+
+
+
diff --git a/dotnet/src/dotnetcore/Providers/Messaging/GXAzureEventGrid/GXAzureEventGrid.csproj b/dotnet/src/dotnetcore/Providers/Messaging/GXAzureEventGrid/GXAzureEventGrid.csproj
index 1bacb83af..47d2c6a5e 100644
--- a/dotnet/src/dotnetcore/Providers/Messaging/GXAzureEventGrid/GXAzureEventGrid.csproj
+++ b/dotnet/src/dotnetcore/Providers/Messaging/GXAzureEventGrid/GXAzureEventGrid.csproj
@@ -8,7 +8,7 @@
-
+
diff --git a/dotnet/src/dotnetcore/Providers/Messaging/GXAzureQueue/GXAzureQueue.csproj b/dotnet/src/dotnetcore/Providers/Messaging/GXAzureQueue/GXAzureQueue.csproj
index 543b2cd48..e9956e29f 100644
--- a/dotnet/src/dotnetcore/Providers/Messaging/GXAzureQueue/GXAzureQueue.csproj
+++ b/dotnet/src/dotnetcore/Providers/Messaging/GXAzureQueue/GXAzureQueue.csproj
@@ -8,7 +8,7 @@
-
+
diff --git a/dotnet/src/dotnetcore/Providers/Messaging/GXAzureServiceBus/GXAzureServiceBus.csproj b/dotnet/src/dotnetcore/Providers/Messaging/GXAzureServiceBus/GXAzureServiceBus.csproj
index 15a2e62da..251919cc3 100644
--- a/dotnet/src/dotnetcore/Providers/Messaging/GXAzureServiceBus/GXAzureServiceBus.csproj
+++ b/dotnet/src/dotnetcore/Providers/Messaging/GXAzureServiceBus/GXAzureServiceBus.csproj
@@ -8,7 +8,7 @@
-
+
diff --git a/dotnet/src/dotnetcore/Providers/Messaging/GXEventRouter/GXEventRouter.csproj b/dotnet/src/dotnetcore/Providers/Messaging/GXEventRouter/GXEventRouter.csproj
index c2c2ccf43..cdf1f3ab0 100644
--- a/dotnet/src/dotnetcore/Providers/Messaging/GXEventRouter/GXEventRouter.csproj
+++ b/dotnet/src/dotnetcore/Providers/Messaging/GXEventRouter/GXEventRouter.csproj
@@ -7,6 +7,10 @@
TRACE;DEBUG;NETCORE
+
+
+
+
diff --git a/dotnet/src/dotnetcore/Providers/Messaging/GXMessageBroker/GXMessageBroker.csproj b/dotnet/src/dotnetcore/Providers/Messaging/GXMessageBroker/GXMessageBroker.csproj
index 2cc2f4242..d40bc0234 100644
--- a/dotnet/src/dotnetcore/Providers/Messaging/GXMessageBroker/GXMessageBroker.csproj
+++ b/dotnet/src/dotnetcore/Providers/Messaging/GXMessageBroker/GXMessageBroker.csproj
@@ -7,6 +7,11 @@
TRACE;DEBUG;NETCORE
+
+
+
+
+
diff --git a/dotnet/src/dotnetcore/Providers/Messaging/GXQueue/GXQueue.csproj b/dotnet/src/dotnetcore/Providers/Messaging/GXQueue/GXQueue.csproj
index 4779d04dc..bce3bd249 100644
--- a/dotnet/src/dotnetcore/Providers/Messaging/GXQueue/GXQueue.csproj
+++ b/dotnet/src/dotnetcore/Providers/Messaging/GXQueue/GXQueue.csproj
@@ -5,7 +5,11 @@
TRACE;DEBUG;NETCORE
GeneXus.Message.Queue
-
+
+
+
+
+
diff --git a/dotnet/src/dotnetcore/Providers/OpenTelemetry/Diagnostics/GXOtel.Diagnostics/GXOtel.Diagnostics.csproj b/dotnet/src/dotnetcore/Providers/OpenTelemetry/Diagnostics/GXOtel.Diagnostics/GXOtel.Diagnostics.csproj
index 8c6a62190..ef080b861 100644
--- a/dotnet/src/dotnetcore/Providers/OpenTelemetry/Diagnostics/GXOtel.Diagnostics/GXOtel.Diagnostics.csproj
+++ b/dotnet/src/dotnetcore/Providers/OpenTelemetry/Diagnostics/GXOtel.Diagnostics/GXOtel.Diagnostics.csproj
@@ -12,8 +12,8 @@
-
-
+
+
diff --git a/dotnet/src/dotnetcore/Providers/OpenTelemetry/Diagnostics/GXOtel.Diagnostics/OtelSpan.cs b/dotnet/src/dotnetcore/Providers/OpenTelemetry/Diagnostics/GXOtel.Diagnostics/OtelSpan.cs
index 4b35201d4..d05c93aa8 100644
--- a/dotnet/src/dotnetcore/Providers/OpenTelemetry/Diagnostics/GXOtel.Diagnostics/OtelSpan.cs
+++ b/dotnet/src/dotnetcore/Providers/OpenTelemetry/Diagnostics/GXOtel.Diagnostics/OtelSpan.cs
@@ -1,112 +1,183 @@
using System;
using System.Diagnostics;
-using GeneXus.Application;
+using GeneXus.Attributes;
+using OpenTelemetry;
using OpenTelemetry.Trace;
-using static GeneXus.OpenTelemetry.Diagnostics.OtelTracer;
namespace GeneXus.OpenTelemetry.Diagnostics
{
+ [GXApi]
public class OtelSpan
{
private Activity _activity;
-
public enum SpanStatusCode
{
Unset,
Ok,
Error
}
- public string Id
- { get => _activity?.Id; }
- public bool IsAllDataRequested
+ internal OtelSpan(Activity activity)
{
- get
- {
- if (_activity != null)
- return _activity.IsAllDataRequested;
- return false;
- }
+ _activity = activity;
}
- public bool IsStopped
- { get
- {
- if (_activity != null )
- return _activity.IsStopped;
- return false;
- }
- }
+ public OtelSpan()
+ {}
- public short Kind
- { get => (short)_activity?.Kind; }
+ public Activity Activity => _activity;
- public string ParentId
- { get => _activity?.ParentId; }
+ #region EO Properties
+ public GXSpanContext SpanContext => _activity == null ? null : new GXSpanContext(_activity.Context);
- public string ParentSpanId
- { get => _activity?.ParentSpanId.ToString(); }
+ public GXTraceContext GetContext => _activity == null ? null : new GXTraceContext(_activity.Context);
+ public string SpanId => _activity?.Id;
- public string TraceId
- { get => _activity?.TraceId.ToString(); }
+ public string TraceId => _activity?.TraceId.ToHexString();
- public short Status
- { get => (short)_activity?.Status; }
-
- internal OtelSpan(Activity activity)
+ #endregion
+
+ #region Methods
+ public void Stop()
{
- _activity = activity;
+ _activity?.Stop();
+ }
+ public void RecordException(string message)
+ {
+ _activity?.RecordException(new Exception(message));
}
- public OtelSpan()
+ public void SetStringAttribute(string property, string value)
{
- _activity = Activity.Current;
+ _activity?.SetTag(property, value);
+
}
- public void Start()
+ public void SetLongAttribute(string property, long value)
{
- _activity?.Start();
+ _activity?.SetTag(property, value);
+
}
+ public void SetDoubleAttribute(string property, double value)
+ {
+ _activity?.SetTag(property, value);
- public void Stop()
+ }
+ public void SetBooleanAttribute(string property, bool value)
{
- _activity?.Stop();
+ _activity?.SetTag(property, value);
+
}
- public void RecordException(string message)
+ public GXTraceContext AddBaggage(string property, string value)
+ {
+ Baggage.SetBaggage(property, value);
+ if (_activity != null)
+ return new GXTraceContext(_activity.Context);
+ else return null;
+ }
+
+ public string GetBaggageItem(string property,GXTraceContext gXTraceContext)
+ {
+ return Baggage.GetBaggage(property);
+ }
+ public void SetStatus(SpanStatusCode spanStatusCode, string message)
{
- _activity.RecordException(new Exception(message));
+ _activity?.SetStatus((ActivityStatusCode)spanStatusCode, message);
}
+ public void SetStatus(SpanStatusCode spanStatusCode)
+ {
+ _activity?.SetStatus((ActivityStatusCode)spanStatusCode);
+ }
+
+ #endregion
- public void SetTag(string property, string value)
+ #region Private Methods
+ public bool IsRecording => IsSpanRecording();
+
+ private bool IsSpanRecording()
{
- _activity.SetTag(property, value);
+ if (_activity != null)
+ return _activity.IsAllDataRequested;
+ else
+ return false;
}
- public string GetTagItem(string property)
+ #endregion
+
+ }
+
+ public class GXTraceContext : GXSpanContext
+ {
+ //Dummy class to be compatible with Java.
+ //.NET does not requiere propagating the context explicitly in most of the cases.
+ //https://github.com/open-telemetry/opentelemetry-dotnet/blob/main/src/OpenTelemetry.Api/README.md#baggage-api
+
+ public GXTraceContext(ActivityContext activityContext):base(activityContext) { }
+ public GXTraceContext():base() { }
+ }
+ public class GXSpanContext
+ {
+ private ActivityContext _activityContext;
+ public GXSpanContext()
{
- return _activity.GetTagItem(property).ToString();
+ _activityContext = Activity.Current.Context;
}
- public void AddBaggage(string property, string value)
+
+ public GXSpanContext(ActivityContext activityContext)
{
- _activity.AddBaggage(property, value);
+ _activityContext = activityContext;
}
- public string GetBaggageItem(string property)
+ public ActivityContext ActivityContext { get { return _activityContext; } }
+
+ public string TraceId => GetTraceId();
+
+ public string SpanId => GetSpanId();
+
+ private string GetTraceId()
{
- return _activity.GetBaggageItem(property).ToString();
+ if (_activityContext != null)
+ {
+ ActivityTraceId activityTraceId = _activityContext.TraceId;
+ return activityTraceId.ToHexString();
+ } else return null;
+ }
+ private string GetSpanId()
+ {
+ if (_activityContext != null)
+ {
+ ActivitySpanId activitySpanId = _activityContext.SpanId;
+ return activitySpanId.ToHexString();
+ } else { return null; }
}
- public void SetStatus(SpanStatusCode spanStatusCode, string message)
+ private GXActivityTraceFlags TraceFlags()
{
- _activity.SetStatus((ActivityStatusCode)spanStatusCode, message);
+ if (_activityContext != null) {
+ ActivityTraceFlags activityTraceFlags = _activityContext.TraceFlags;
+ return (GXActivityTraceFlags)activityTraceFlags;
+ }
+ else { return GXActivityTraceFlags.None;}
}
- public SpanStatusCode GetStatus()
+ private string TraceState()
{
- return (SpanStatusCode)_activity.GetStatus().StatusCode;
+ if (_activityContext != null)
+ return _activityContext.TraceState;
+ else return null;
}
- //ToDO
- //public void AddEvent()
- //{
- //}
+ //
+ // Summary:
+ // Specifies flags defined by the W3C standard that are associated with an activity.
+ internal enum GXActivityTraceFlags
+ {
+ //
+ // Summary:
+ // The activity has not been marked.
+ None = 0,
+ //
+ // Summary:
+ // The activity (or more likely its parents) has been marked as useful to record.
+ Recorded = 1
+ }
}
}
diff --git a/dotnet/src/dotnetcore/Providers/OpenTelemetry/Diagnostics/GXOtel.Diagnostics/OtelTracer.cs b/dotnet/src/dotnetcore/Providers/OpenTelemetry/Diagnostics/GXOtel.Diagnostics/OtelTracer.cs
index 8ce722bef..812b1ae28 100644
--- a/dotnet/src/dotnetcore/Providers/OpenTelemetry/Diagnostics/GXOtel.Diagnostics/OtelTracer.cs
+++ b/dotnet/src/dotnetcore/Providers/OpenTelemetry/Diagnostics/GXOtel.Diagnostics/OtelTracer.cs
@@ -1,35 +1,103 @@
+using System;
+using System.Collections.Generic;
using System.Diagnostics;
-using GeneXus.Application;
+using System.Linq;
using GeneXus.Attributes;
+using GeneXus.Services.OpenTelemetry;
namespace GeneXus.OpenTelemetry.Diagnostics
{
[GXApi]
public class OtelTracer
{
+ internal static ActivitySource activitySource;
+ internal static ActivitySource ActivitySource
+ {
+ get
+ {
+ if (activitySource == null)
+ {
+ activitySource = string.IsNullOrEmpty(OpenTelemetryService.GX_ACTIVITY_SOURCE_VERSION) ? new(OpenTelemetryService.GX_ACTIVITY_SOURCE_NAME) : new(OpenTelemetryService.GX_ACTIVITY_SOURCE_NAME, OpenTelemetryService.GX_ACTIVITY_SOURCE_VERSION);
+ }
+ return activitySource;
+ }
+ }
public enum SpanType
{
- Client,
- Consumer,
Internal,
+ Server,
+ Client,
Producer,
- Server
+ Consumer
+ }
+
+ public OtelSpan CreateSpan(string name)
+ {
+ //Returns null when the are no listeners.
+ Activity activity = ActivitySource.StartActivity(name);
+ if (activity != null)
+ return new OtelSpan(activity);
+ return null;
}
public OtelSpan CreateSpan(string name, SpanType kind)
{
- Activity activity = GXBaseObject.ActivitySource.StartActivity(name, (ActivityKind)kind);
+ Activity activity = ActivitySource.StartActivity(name, (ActivityKind)kind);
+ if (activity != null)
+ return new OtelSpan(activity);
+ else
+ return null;
+ }
+
+ public OtelSpan CreateSpan(string name, GXTraceContext gxTraceContext, SpanType spanType)
+ {
+ Activity activity = ActivitySource.StartActivity(name,
+ kind: (ActivityKind)spanType,
+ parentContext: gxTraceContext.ActivityContext);
+ return new OtelSpan(activity);
+
+ }
+
+ public OtelSpan CreateSpan(string name, GXTraceContext gxTraceContext, SpanType spanType, IList gxSpanContexts)
+ {
+ //https://learn.microsoft.com/en-us/dotnet/core/diagnostics/distributed-tracing-instrumentation-walkthroughs#optional-links
+ List contexts = new List();
+
+ foreach (GXSpanContext gxSpanContext in gxSpanContexts)
+ {
+ ActivityContext context = gxSpanContext.ActivityContext;
+ contexts.Add(context);
+ }
+
+ Activity activity = ActivitySource.StartActivity(name,
+ kind: (ActivityKind)spanType,
+ parentContext: gxTraceContext.ActivityContext,
+ links: contexts.Select(ctx => new ActivityLink(ctx)));
return new OtelSpan(activity);
}
- public OtelSpan GetCurrent()
+ public OtelSpan CreateSpan(string name, GXTraceContext gxTraceContext, SpanType spanType, IList gxSpanContexts, DateTime dateTime)
{
- return new OtelSpan(Activity.Current);
+ //https://learn.microsoft.com/en-us/dotnet/core/diagnostics/distributed-tracing-instrumentation-walkthroughs#optional-links
+ List contexts = new List();
+
+ foreach (GXSpanContext gxSpanContext in gxSpanContexts)
+ {
+ ActivityContext context = gxSpanContext.ActivityContext;
+ contexts.Add(context);
+ }
+
+ Activity activity = ActivitySource.StartActivity(name,
+ kind: (ActivityKind)spanType,
+ parentContext: gxTraceContext.ActivityContext,
+ links: contexts.Select(ctx => new ActivityLink(ctx)),
+ startTime:dateTime);
+ return new OtelSpan(activity);
}
- public bool HasListeners()
+ public static OtelSpan GetCurrent()
{
- return GXBaseObject.ActivitySource.HasListeners();
+ return new OtelSpan(Activity.Current);
}
}
}
diff --git a/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetry/GXMeterProviderBuilder.cs b/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetry/GXMeterProviderBuilder.cs
new file mode 100644
index 000000000..0e5630c85
--- /dev/null
+++ b/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetry/GXMeterProviderBuilder.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using OpenTelemetry.Metrics;
+using OpenTelemetry.Trace;
+
+namespace GeneXus.OpenTelemetry
+{
+ public static class GXMeterProviderBuilder
+ {
+ public static MeterProviderBuilder AddGxMeterAspNetInstrumentation(this MeterProviderBuilder meter)
+ {
+ string envvar = Environment.GetEnvironmentVariable("OTEL_METRICS_EXPORTER");
+ meter
+ .AddAspNetCoreInstrumentation()
+ .AddHttpClientInstrumentation()
+ .AddRuntimeInstrumentation()
+ .AddOtlpExporter();
+ if (envvar != null && envvar.Contains("console"))
+ meter.AddConsoleExporter();
+ return meter;
+ }
+
+ }
+}
diff --git a/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetry/GeneXus.OpenTelemetry.csproj b/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetry/GeneXus.OpenTelemetry.csproj
index 6c23dac29..171a608cd 100644
--- a/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetry/GeneXus.OpenTelemetry.csproj
+++ b/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetry/GeneXus.OpenTelemetry.csproj
@@ -1,21 +1,20 @@
-
- net6.0;net8.0
- enable
- enable
- GeneXus.OpenTelemetry
- OpenTelemetry GeneXus DotNet
- GeneXus.OpenTelemetry.OpenTelemetry
- NU1605
-
+
+ net6.0;net8.0
+ GeneXus.OpenTelemetry
+ OpenTelemetry GeneXus DotNet
+ GeneXus.OpenTelemetry.OpenTelemetry
+ NU1605
+
-
-
-
-
-
+
+
+
+
+
+
diff --git a/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetry/GxTraceProviderBuilder.cs b/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetry/GxTraceProviderBuilder.cs
index 58da3b8d2..124a86da6 100644
--- a/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetry/GxTraceProviderBuilder.cs
+++ b/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetry/GxTraceProviderBuilder.cs
@@ -1,3 +1,4 @@
+using System;
using GeneXus.Services.OpenTelemetry;
using OpenTelemetry.Trace;
@@ -19,17 +20,15 @@ public static TracerProviderBuilder AddGxAspNetInstrumentation(this TracerProvid
{
opt.RecordException = true;
})
- .AddAspNetCoreInstrumentation(opt =>
- {
- opt.RecordException = true;
- })
.AddSqlClientInstrumentation(opt =>
{
opt.RecordException = true;
opt.EnableConnectionLevelAttributes = true;
opt.SetDbStatementForText = true;
- })
- .AddConsoleExporter();
+ });
+ string envvar = Environment.GetEnvironmentVariable("OTEL_TRACES_EXPORTER");
+ if (envvar != null && envvar.Contains("console"))
+ tracer.AddConsoleExporter();
return tracer;
}
}
diff --git a/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetry/OpenTelemetryProvider.cs b/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetry/OpenTelemetryProvider.cs
index 78eaf649e..c7c237dcf 100644
--- a/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetry/OpenTelemetryProvider.cs
+++ b/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetry/OpenTelemetryProvider.cs
@@ -2,23 +2,41 @@
using GeneXus.Services.OpenTelemetry;
using Microsoft.Extensions.DependencyInjection;
using OpenTelemetry.Trace;
+using OpenTelemetry.Metrics;
+using OpenTelemetry.Resources;
+using System;
+using Microsoft.IdentityModel.Tokens;
namespace GeneXus.OpenTelemetry.OpenTelemetry
{
public class OpenTelemetryProvider : IOpenTelemetryProvider
{
+ const string OTEL_METRICS_EXPORTER = "OTEL_METRICS_EXPORTER";
+ const string OTEL_TRACES_EXPORTER = "OTEL_TRACES_EXPORTER";
public OpenTelemetryProvider(GXService s)
{
}
public bool InstrumentAspNetCoreApplication(IServiceCollection services)
{
- services.AddOpenTelemetry().WithTracing(tracerProviderBuilder =>
- {
- tracerProviderBuilder
- .AddOtlpExporter()
- .AddGxAspNetInstrumentation();
- });
+ string envvar = Environment.GetEnvironmentVariable(OTEL_TRACES_EXPORTER);
+ if (envvar.IsNullOrEmpty() || !envvar.ToLower().Equals("none")) {
+ services.AddOpenTelemetry().WithTracing(tracerProviderBuilder =>
+ {
+ tracerProviderBuilder
+ .AddOtlpExporter()
+ .AddGxAspNetInstrumentation();
+ });
+ }
+
+ envvar = Environment.GetEnvironmentVariable(OTEL_METRICS_EXPORTER);
+ if (envvar.IsNullOrEmpty() || !envvar.ToLower().Equals("none")) {
+ services.AddOpenTelemetry().WithMetrics(metricsProviderBuilder =>
+ {
+ metricsProviderBuilder
+ .AddGxMeterAspNetInstrumentation();
+ });
+ }
return true;
}
}
diff --git a/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryAWSOtel/AWSOtelProvider.cs b/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryAWSOtel/AWSOtelProvider.cs
index 2aae5321f..7de8b29f1 100644
--- a/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryAWSOtel/AWSOtelProvider.cs
+++ b/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryAWSOtel/AWSOtelProvider.cs
@@ -1,12 +1,11 @@
using System;
+using GeneXus.Services;
using GeneXus.Services.OpenTelemetry;
using Microsoft.Extensions.DependencyInjection;
using OpenTelemetry;
-using OpenTelemetry.Trace;
using OpenTelemetry.Contrib.Extensions.AWSXRay.Trace;
-using GeneXus.Services;
-using GeneXus.Services.Common;
-using GeneXus.Diagnostics;
+using OpenTelemetry.Metrics;
+using OpenTelemetry.Trace;
namespace GeneXus.OpenTelemetry.AWS
{
@@ -33,6 +32,10 @@ public bool InstrumentAspNetCoreApplication(IServiceCollection _)
.AddGxAspNetInstrumentation()
.Build();
+ Sdk.CreateMeterProviderBuilder()
+ .AddGxMeterAspNetInstrumentation()
+ .Build();
+
Sdk.SetDefaultTextMapPropagator(new AWSXRayPropagator());
return true;
diff --git a/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryAzureMonitor/AzureAppInsights.cs b/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryAzureMonitor/AzureAppInsights.cs
index 98d6c7d1c..96c804a64 100644
--- a/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryAzureMonitor/AzureAppInsights.cs
+++ b/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryAzureMonitor/AzureAppInsights.cs
@@ -56,6 +56,7 @@ public bool InstrumentAspNetCoreApplication(IServiceCollection _)
GXLogging.Debug(log, "Connect to Azure monitor Opentelemetry Metrics exporter using Default Azure credential");
}
})
+ .AddGxMeterAspNetInstrumentation()
.Build();
return true;
}
diff --git a/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryAzureMonitor/GeneXus.OpenTelemetry.Azure.AppInsights.csproj b/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryAzureMonitor/GeneXus.OpenTelemetry.Azure.AppInsights.csproj
index 12ae1791b..6fcea0822 100644
--- a/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryAzureMonitor/GeneXus.OpenTelemetry.Azure.AppInsights.csproj
+++ b/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryAzureMonitor/GeneXus.OpenTelemetry.Azure.AppInsights.csproj
@@ -11,13 +11,13 @@
-
-
+
+
-
+
-
-
+
+
diff --git a/dotnet/src/dotnetcore/Providers/Storage/GXAmazonS3/GXAmazonS3.csproj b/dotnet/src/dotnetcore/Providers/Storage/GXAmazonS3/GXAmazonS3.csproj
index 2e1e3ce13..ca162324b 100644
--- a/dotnet/src/dotnetcore/Providers/Storage/GXAmazonS3/GXAmazonS3.csproj
+++ b/dotnet/src/dotnetcore/Providers/Storage/GXAmazonS3/GXAmazonS3.csproj
@@ -13,6 +13,7 @@
+
diff --git a/dotnet/src/dotnetcore/Providers/Storage/GXAzureStorage/GXAzureStorage.csproj b/dotnet/src/dotnetcore/Providers/Storage/GXAzureStorage/GXAzureStorage.csproj
index 099f6efb1..62f614c32 100644
--- a/dotnet/src/dotnetcore/Providers/Storage/GXAzureStorage/GXAzureStorage.csproj
+++ b/dotnet/src/dotnetcore/Providers/Storage/GXAzureStorage/GXAzureStorage.csproj
@@ -15,6 +15,7 @@
+
diff --git a/dotnet/src/dotnetcore/Providers/Storage/GXGoogleCloud/GXGoogleCloud.csproj b/dotnet/src/dotnetcore/Providers/Storage/GXGoogleCloud/GXGoogleCloud.csproj
index 0c3bfd68c..1af8b36e6 100644
--- a/dotnet/src/dotnetcore/Providers/Storage/GXGoogleCloud/GXGoogleCloud.csproj
+++ b/dotnet/src/dotnetcore/Providers/Storage/GXGoogleCloud/GXGoogleCloud.csproj
@@ -14,6 +14,7 @@
+
diff --git a/dotnet/src/dotnetcore/Reor/Reor.csproj b/dotnet/src/dotnetcore/Reor/Reor.csproj
index 59e7feedf..7743e4090 100644
--- a/dotnet/src/dotnetcore/Reor/Reor.csproj
+++ b/dotnet/src/dotnetcore/Reor/Reor.csproj
@@ -16,6 +16,10 @@
+
+
+
+
diff --git a/dotnet/src/dotnetframework/GxClasses/Configuration/ExternalStorage.cs b/dotnet/src/dotnetframework/GxClasses/Configuration/ExternalStorage.cs
index d30bdc160..6357186e2 100644
--- a/dotnet/src/dotnetframework/GxClasses/Configuration/ExternalStorage.cs
+++ b/dotnet/src/dotnetframework/GxClasses/Configuration/ExternalStorage.cs
@@ -15,7 +15,7 @@ public class ExternalStorage : GxStorageProvider
private GXService providerService;
- static readonly IGXLogger logger = GXLoggerFactory.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName);
+ static readonly IGXLogger logger = GXLoggerFactory.GetLogger();
public ExternalStorage()
{
diff --git a/dotnet/src/dotnetframework/GxClasses/Core/GXApplication.cs b/dotnet/src/dotnetframework/GxClasses/Core/GXApplication.cs
index 08d94ef70..8ec7b8ee8 100644
--- a/dotnet/src/dotnetframework/GxClasses/Core/GXApplication.cs
+++ b/dotnet/src/dotnetframework/GxClasses/Core/GXApplication.cs
@@ -21,7 +21,9 @@ namespace GeneXus.Application
#endif
using GeneXus.Configuration;
using GeneXus.Metadata;
+#if !NETCORE
using Jayrock.Json;
+#endif
using GeneXus.Http;
using System.Collections.Specialized;
using System.Collections.Generic;
@@ -47,6 +49,8 @@ namespace GeneXus.Application
using System.Security.Claims;
using System.Security;
using Microsoft.Net.Http.Headers;
+ using System.Threading.Tasks;
+ using GeneXus.Data.ADO;
public interface IGxContext
{
@@ -320,7 +324,6 @@ internal class GxApplication
public class GxContext : IGxContext
{
private static IGXLogger log = null;
- internal static bool configurationLoaded = Config.configLoaded;
internal static string GX_SPA_REQUEST_HEADER = "X-SPA-REQUEST";
internal static string GX_SPA_REDIRECT_URL = "X-SPA-REDIRECT-URL";
internal const string GXLanguage = "GXLanguage";
@@ -1406,12 +1409,27 @@ public IGxDataStore GetDataStore(string id)
return ds;
return null;
}
+#if NETCORE
+ internal async Task CloseConnectionsAsync()
+ {
+ GxUserInfo.RemoveHandle(this.handle);
+ foreach (GxDataStore ds in _DataStores)
+ await ds.CloseConnectionsAsync();
+
+ CloseConnectionsResources();
+ }
+#endif
public void CloseConnections()
{
GxUserInfo.RemoveHandle(this.handle);
foreach (IGxDataStore ds in _DataStores)
ds.CloseConnections();
+ CloseConnectionsResources();
+ }
+
+ private void CloseConnectionsResources()
+ {
if (_reportHandlerToClose != null)
{
for (int i = 0; i < _reportHandlerToClose.Count; i++)
diff --git a/dotnet/src/dotnetframework/GxClasses/Core/GXUtils.cs b/dotnet/src/dotnetframework/GxClasses/Core/GXUtils.cs
index db1162f17..7b029ad64 100644
--- a/dotnet/src/dotnetframework/GxClasses/Core/GXUtils.cs
+++ b/dotnet/src/dotnetframework/GxClasses/Core/GXUtils.cs
@@ -771,6 +771,7 @@ public short dfwnext()
return GX_ASCDEL_WRITEERROR;
}
}
+ const int MAX_DECIMAL_PRECISION = 29;
public short dfwpnum(decimal num, int dec)
{
if (_writeStatus == FileIOStatus.Closed)
@@ -778,7 +779,7 @@ public short dfwpnum(decimal num, int dec)
GXLogging.Error(log, "Error ADF0004");
return GX_ASCDEL_INVALIDSEQUENCE;
}
- appendFld(StringUtil.Str(num, 18, dec).TrimStart(null));
+ appendFld(StringUtil.Str(num, MAX_DECIMAL_PRECISION, dec).TrimStart(null));
return GX_ASCDEL_SUCCESS;
}
public short dfwptxt(string s, int len)
diff --git a/dotnet/src/dotnetframework/GxClasses/Core/GXUtilsCommon.cs b/dotnet/src/dotnetframework/GxClasses/Core/GXUtilsCommon.cs
index 9f2919a80..21cf5a104 100644
--- a/dotnet/src/dotnetframework/GxClasses/Core/GXUtilsCommon.cs
+++ b/dotnet/src/dotnetframework/GxClasses/Core/GXUtilsCommon.cs
@@ -665,18 +665,83 @@ private enum FORMAT_SECTION
NEGATIVE_VALUES = -1,
ZEROS = 0
}
- private static string GxPictureToNetPicture(string gxpicture, bool separatorsAsLiterals, FORMAT_SECTION section)
+ const char ESCAPE_CHARACTER = '\\';
+ const char NUMBER_SIGN = '#';
+ const char QUESTION_MARK = '?';
+ const char BLANK = ' ';
+ static bool EscapedSymbol(string picture, int idx)
+ {
+ return (idx > 0 && picture[idx - 1] == ESCAPE_CHARACTER);
+ }
+ static int LeadingBlanks(string gxpicture)
+ {
+ int leadingBlanks = 0;
+ bool inBlanks = false;
+ if (gxpicture.Contains(QUESTION_MARK)) {
+ for (int i = 0; i < gxpicture.Length; i++)
+ {
+ if (gxpicture[i] == QUESTION_MARK && !EscapedSymbol(gxpicture, i))
+ {
+ inBlanks = true;
+ }
+ else if ((gxpicture[i] == '.') && !EscapedSymbol(gxpicture, i))
+ {
+ inBlanks = false;
+ break;
+ }
+
+ if (inBlanks)
+ leadingBlanks++;
+ }
+ }
+ return leadingBlanks;
+ }
+ static int TrailingBlanks(string gxpicture, out bool decimalsAsBlank)
+ {
+ int trailingBlanks = 0;
+ int sep = gxpicture.IndexOf('.');
+ decimalsAsBlank = false;
+ if (sep >= 0)
+ {
+ string rightPic = gxpicture.Substring(sep);
+ if (rightPic.Contains(QUESTION_MARK))
+ {
+ for (int i = gxpicture.Length-1; i >= 0; i--)
+ {
+ if (gxpicture[i] == QUESTION_MARK && !EscapedSymbol(gxpicture, i))
+ trailingBlanks++;
+ else if (gxpicture[i] == '.')
+ {
+ decimalsAsBlank = true;
+ break;
+ }
+ else if (gxpicture[i] == NUMBER_SIGN || gxpicture[i] == 'Z' || gxpicture[i] == '9')
+ {
+ decimalsAsBlank = false;
+ break;
+ }
+ }
+ }
+ }
+ return trailingBlanks;
+ }
+
+ private static string GxPictureToNetPicture(string gxpicture, bool separatorsAsLiterals, FORMAT_SECTION section, int digits, int decimals)
{
if (string.IsNullOrEmpty(gxpicture))
return string.Empty;
bool inDecimals = false;
StringBuilder strPicture = new StringBuilder("{0,");
- strPicture.Append(gxpicture.Length);
+ strPicture.Append(PictureLength(gxpicture));
strPicture.Append(':');
bool blankwhenzero = true;
bool explicitSign = (gxpicture[0] == '+');
bool withoutMinusSign = (gxpicture[0] == '(' && gxpicture[gxpicture.Length - 1] == ')') || gxpicture.EndsWith("DB") || explicitSign;
+ int totalLeadingBlanks = LeadingBlanks(gxpicture);
+ int totalRighBlanks = TrailingBlanks(gxpicture, out bool decimalsAsBlank);
+ int lBlanks = 0;
+ int rDigits = 0;
if (section == FORMAT_SECTION.NEGATIVE_VALUES && withoutMinusSign)
//If it has a sign, then use the first section (which by default assigns only a negative sign).
@@ -688,9 +753,34 @@ private static string GxPictureToNetPicture(string gxpicture, bool separatorsAsL
strPicture.Append(';');//Section Separator.
strPicture.Append(';');//Section Separator.
}
+ if (gxpicture.IndexOf(ESCAPE_CHARACTER) >= 0)
+ {
+ StringBuilder gxEscapedPicture = new StringBuilder();
+ for (int i = 0; i < gxpicture.Length; i++)
+ {
+
+ if (gxpicture[i] == ESCAPE_CHARACTER && i + 1 < gxpicture.Length && gxpicture[i + 1] == ESCAPE_CHARACTER)
+ {
+ gxEscapedPicture.Append(ESCAPE_CHARACTER);
+ gxEscapedPicture.Append(ESCAPE_CHARACTER);
+ gxEscapedPicture.Append(ESCAPE_CHARACTER);
+ i++;
+ }
+ else
+ {
+ gxEscapedPicture.Append(gxpicture[i]);
+ }
+ }
+ gxpicture = gxEscapedPicture.ToString();
+ }
for (int i = 0; i < gxpicture.Length; i++)
{
- if (gxpicture[i] == 'Z')
+ bool inLiteral = EscapedSymbol(gxpicture, i);
+ if (inLiteral || gxpicture[i] == ESCAPE_CHARACTER)
+ {
+ strPicture.Append(gxpicture[i]);
+ }
+ else if (gxpicture[i] == 'Z')
{
if (inDecimals)
{
@@ -701,7 +791,39 @@ private static string GxPictureToNetPicture(string gxpicture, bool separatorsAsL
strPicture.Append('0');
}
else
- strPicture.Append('#');
+ strPicture.Append(NUMBER_SIGN);
+ }
+ else if (gxpicture[i] == NUMBER_SIGN)
+ {
+ strPicture.Append(NUMBER_SIGN);
+ }
+ else if (gxpicture[i] == QUESTION_MARK)
+ {
+ if (inDecimals)
+ {
+ if (rDigits >= decimals && rDigits < totalRighBlanks)
+ {
+ strPicture.Append(BLANK);
+ }
+ else
+ {
+ strPicture.Append(NUMBER_SIGN);
+ }
+ rDigits++;
+ }
+ else
+ {
+ if (lBlanks < (totalLeadingBlanks - digits))
+ {
+ strPicture.Append(BLANK);
+ lBlanks++;
+ }
+ else
+ {
+ strPicture.Append(NUMBER_SIGN);
+ }
+ }
+
}
else if (gxpicture[i] == '9')
{
@@ -711,11 +833,20 @@ private static string GxPictureToNetPicture(string gxpicture, bool separatorsAsL
else if (gxpicture[i] == '.')
{
inDecimals = true;
- if (i > 0 && strPicture[strPicture.Length - 1] == '#') strPicture[strPicture.Length - 1] = '0';
+ if (i > 0 && strPicture[strPicture.Length - 1] == NUMBER_SIGN) strPicture[strPicture.Length - 1] = '0';
if (separatorsAsLiterals)
strPicture.Append("\".\"");
else
- strPicture.Append(gxpicture[i]);
+ {
+ if (decimalsAsBlank && decimals == 0)
+ {
+ strPicture.Append(BLANK); //Replace decimal separator by blank
+ }
+ else
+ {
+ strPicture.Append(gxpicture[i]);
+ }
+ }
}
else if (gxpicture[i] == ',')
{
@@ -724,6 +855,10 @@ private static string GxPictureToNetPicture(string gxpicture, bool separatorsAsL
else
strPicture.Append(gxpicture[i]);
}
+ else if (gxpicture[i] == BLANK)
+ {
+ strPicture.Append(BLANK);
+ }
else
{
//0,#,.,,,%,E0, E+0, E-0,e0,e+0,e-0, are characters with special meaning in Custom Numeric Format Strings of .net
@@ -740,7 +875,7 @@ private static string GxPictureToNetPicture(string gxpicture, bool separatorsAsL
//Pictures (99.9) => 12.5 -12.5
if (section != FORMAT_SECTION.NEGATIVE_VALUES && withoutMinusSign && (i == 0 || i == gxpicture.Length - 1))
{
- strPicture.Append(' ');
+ strPicture.Append(BLANK);
}
else
{
@@ -751,7 +886,7 @@ private static string GxPictureToNetPicture(string gxpicture, bool separatorsAsL
//Pictures +99.9 => +12.5 -12.5
if (explicitSign && i == 0 && section == FORMAT_SECTION.ZEROS)
{
- strPicture.Append(' ');
+ strPicture.Append(BLANK);
}
else if (explicitSign && i == 0 && section == FORMAT_SECTION.NEGATIVE_VALUES)
{
@@ -769,7 +904,7 @@ private static string GxPictureToNetPicture(string gxpicture, bool separatorsAsL
if (section == FORMAT_SECTION.POSITIVE_VALUES)
strPicture.Append('C');
else
- strPicture.Append(' ');
+ strPicture.Append(BLANK);
}
else
{
@@ -782,7 +917,7 @@ private static string GxPictureToNetPicture(string gxpicture, bool separatorsAsL
if (section == FORMAT_SECTION.POSITIVE_VALUES)
strPicture.Append('R');
else
- strPicture.Append(' ');
+ strPicture.Append(BLANK);
}
else
{
@@ -799,14 +934,29 @@ private static string GxPictureToNetPicture(string gxpicture, bool separatorsAsL
}
if (blankwhenzero && section == FORMAT_SECTION.ZEROS)//Z,ZZZ,ZZZ.ZZ format 0.00 to "". sac.20145
{
- return Replicate(' ', gxpicture.Length);
+ return Replicate(BLANK, gxpicture.Length);
}
else
{
return strPicture.Append('}').ToString();
}
}
- static bool useLiteralSeparators(string gxpicture)
+
+ private static int PictureLength(string gxpicture)
+ {
+ int count = 0;
+ if (gxpicture.Contains(ESCAPE_CHARACTER))
+ {
+ foreach (char ch in gxpicture)
+ {
+ if (ch == ESCAPE_CHARACTER)
+ count++;
+ }
+ }
+ return gxpicture.Length - count;
+ }
+
+ static bool UseLiteralSeparators(string gxpicture)
{
// If it has non-numerical characters, then the separators are used as literals
@@ -861,7 +1011,7 @@ public static string GetPictureFromLD(int digits, int decimals, int thousandSepL
}
else
{
- str.Append('#');
+ str.Append(NUMBER_SIGN);
}
}
}
@@ -877,14 +1027,14 @@ public static string GetPictureFromLD(int digits, int decimals, int thousandSepL
}
public static string Concat(string init, string last)
{
- char[] trimChars = { ' ' };
+ char[] trimChars = { BLANK };
StringBuilder fmtString = new StringBuilder(init.TrimEnd(trimChars));
fmtString.Append(last);
return fmtString.ToString();
}
public static string Concat(string init, string last, string separator)
{
- char[] trimChars = { ' ' };
+ char[] trimChars = { BLANK };
StringBuilder fmtString = new StringBuilder(init.TrimEnd(trimChars));
fmtString.Append(separator);
fmtString.Append(last);
@@ -1011,7 +1161,7 @@ static public int Len(string s)
static public string Trim(string s)
{
if (!string.IsNullOrEmpty(s))
- return s.Trim(' ');
+ return s.Trim(BLANK);
else
return s;
}
@@ -1020,8 +1170,8 @@ static public string RTrim(string s)
if (!string.IsNullOrEmpty(s))
{
int len = s.Length;
- if (len > 0 && s[len - 1] == ' ')
- return s.TrimEnd(' ');
+ if (len > 0 && s[len - 1] == BLANK)
+ return s.TrimEnd(BLANK);
else
return s;
}
@@ -1033,8 +1183,8 @@ static public string LTrim(string s)
if (!string.IsNullOrEmpty(s))
{
int len = s.Length;
- if (len > 0 && s[0] == ' ')
- return s.TrimStart(' ');
+ if (len > 0 && s[0] == BLANK)
+ return s.TrimStart(BLANK);
else
return s;
}
@@ -1166,9 +1316,14 @@ public string Format(decimal value, string gxpicture)
{
section = FORMAT_SECTION.ZEROS;
}
- bool separatorsAsLiterals = useLiteralSeparators(gxpicture);
+ bool separatorsAsLiterals = UseLiteralSeparators(gxpicture);
+ string invariantStrValue = Math.Abs(value).ToString(CultureInfo.InvariantCulture.NumberFormat);
+ int decSeparatorIdx = invariantStrValue.IndexOf(CultureInfo.InvariantCulture.NumberFormat.NumberDecimalSeparator);
+
+ int digits = WholeDigits(value, invariantStrValue, decSeparatorIdx);
+ int decimals = DecimalDigits(invariantStrValue, decSeparatorIdx);
- string picture = GxPictureToNetPicture(gxpicture, separatorsAsLiterals, section);
+ string picture = GxPictureToNetPicture(gxpicture, separatorsAsLiterals, section, digits, decimals);
//It must consider format because it can have other types of characters that are not Z or 9 or. neither ,.
string res;
if (!string.IsNullOrEmpty(picture))
@@ -1177,7 +1332,7 @@ public string Format(decimal value, string gxpicture)
}
else
{
- res = value.ToString(CultureInfo.InvariantCulture.NumberFormat);
+ res = invariantStrValue;
}
if (separatorsAsLiterals)
{
@@ -1191,7 +1346,35 @@ public string Format(decimal value, string gxpicture)
{
return ReplaceSeparators(res, numFmtInfo.NumberDecimalSeparator, numFmtInfo.NumberGroupSeparator);
}
+ }
+ int DecimalDigits(string invariantStrValue, int decSeparatorIdx)
+ {
+ int decimals;
+ if (decSeparatorIdx < 0)
+ decimals = 0;
+ else
+ decimals = invariantStrValue.Length - decSeparatorIdx - 1;
+ return decimals;
+ }
+ int WholeDigits(decimal value, string invariantStrValue, int decSeparatorIdx)
+ {
+ int digits;
+ if (value == 0)
+ {
+ digits = 1;
+ }
+ else if (value < 1 && value >= 0)
+ digits = 0;
+ else if (decSeparatorIdx < 0)
+ {
+ digits = invariantStrValue.Length;
+ }
+ else
+ {
+ digits = decSeparatorIdx;
+ }
+ return digits;
}
string FormatNumber(string s, string p)
{
@@ -1218,7 +1401,7 @@ string FormatNumber(string s, string p, string decSep, string thousandsSep)
pStart = p.Length;
else
pDec = p.Length - pStart; // decimal count (including point)
- StringBuilder result = new StringBuilder(new string(' ', Math.Max(p.Length, s.Length)));
+ StringBuilder result = new StringBuilder(new string(BLANK, Math.Max(p.Length, s.Length)));
// Process the left of the decimal point
j = sStart - 1;
k = pStart - 1;
@@ -1229,7 +1412,7 @@ string FormatNumber(string s, string p, string decSep, string thousandsSep)
case '9':
if (j < 0)
result[k--] = '0';
- else if (s[j] == ' ')
+ else if (s[j] == BLANK)
result[k--] = '0';
else
result[k--] = s[j];
@@ -1237,10 +1420,10 @@ string FormatNumber(string s, string p, string decSep, string thousandsSep)
break;
case 'Z':
if (j < 0)
- result[k--] = ' ';
+ result[k--] = BLANK;
else if (leftZ || leftZero(s, j))
{
- result[k--] = ' ';
+ result[k--] = BLANK;
leftZ = true;
}
else
@@ -1288,7 +1471,7 @@ string FormatNumber(string s, string p, string decSep, string thousandsSep)
case 'Z':
if (rightZ || rightZero(s, j))
{
- result[i] = ' ';
+ result[i] = BLANK;
rightZ = true;
}
else if (j < s.Length)
@@ -1392,7 +1575,7 @@ static public bool ToBoolean(string value)
}
public static string Space(int spaces)
{
- return new string(' ', spaces);
+ return new string(BLANK, spaces);
}
public static string Right(string text, int size)
{
@@ -1418,7 +1601,7 @@ public static string Left(string text, int size)
static public bool Like(string str, string ptrn)
{
- return Like(str, ptrn, ' ');
+ return Like(str, ptrn, BLANK);
}
static public bool Like(string str, string ptrn, char escape)
{
@@ -1443,10 +1626,10 @@ static public bool Like(string str, string ptrn, char escape)
srchPtr = 0;
scapeCount = 0;
- wildChr = ' ';
- srchChr = ' ';
+ wildChr = BLANK;
+ srchChr = BLANK;
- bool useEscape = escape != ' ';
+ bool useEscape = escape != BLANK;
bool isEscape = false;
bool applyEscape = false;
@@ -1463,7 +1646,7 @@ static public bool Like(string str, string ptrn, char escape)
if (srchPtr <= srchLen)
srchChr = str[srchPtr - scapeCount];
else
- srchChr = ' ';
+ srchChr = BLANK;
}
if (isEscape)
@@ -1624,7 +1807,7 @@ public static string JSONEncode(string s)
sb.Append("\\r");
else
{
- if (ch < ' ')
+ if (ch < BLANK)
{
sb.Append("\\u");
sb.Append(((int)ch).ToString("x4", CultureInfo.InvariantCulture));
@@ -2656,18 +2839,18 @@ public static DateTime CToD2(string value)
}
return nullDate;
}
- internal static DateTime CToDT2(string jsonDate) {
+ internal static DateTime CToDT2(string jsonDate, IGxContext context) {
if (!string.IsNullOrEmpty(jsonDate) && (jsonDate.Contains(ISO_8601_TIME_SEPARATOR) || jsonDate.Contains(ISO_8601_TIME_SEPARATOR_1)))
{
- return CToT2(jsonDate);
+ return CToT2(jsonDate, context);
}
else
{
return CToD2(jsonDate);
}
}
- public static DateTime CToT2(string value)
+ public static DateTime CToT2(string value, IGxContext context)
{
if (isNullJsonDate(value))
return nullDate;
@@ -2684,38 +2867,76 @@ public static DateTime CToT2(string value)
}
}
if (Preferences.useTimezoneFix())
- ret = fromUniversalTime(ret);
+ {
+ if (context==null)
+ ret = fromUniversalTime(ret);
+ else
+ ret = fromUniversalTime(ret, context);
+ }
return ret;
}
}
+ //[Obsolete("CToT2 is deprecated, use CToT2(string, IGxContext) instead", false)]
+ public static DateTime CToT2(string value)
+ {
+ return CToT2(value, null);
+ }
+ //[Obsolete("TToC2 is deprecated, use TToC2(DateTime, IGxContext) instead", false)]
public static string TToC2(DateTime dt)
{
return TToC2(dt, true);
}
+ public static string TToC2(DateTime dt, IGxContext context)
+ {
+ return TToC2(dt, true, context);
+ }
+ //[Obsolete("TToC2 is deprecated, use TToC2(DateTime, bool, IGxContext) instead", false)]
public static string TToC2(DateTime dt, bool toUTC)
{
- return TToCRest(dt, "0000-00-00T00:00:00", JsonDateFormat, toUTC);
+ return TToCRest(dt, "0000-00-00T00:00:00", JsonDateFormat, null, toUTC);
+ }
+ internal static string TToC2(DateTime dt, bool toUTC, IGxContext context)
+ {
+ return TToCRest(dt, "0000-00-00T00:00:00", JsonDateFormat, context, toUTC);
}
+ //[Obsolete("TToC3 is deprecated, use TToC3(DateTime, IGxContext) instead", false)]
public static string TToC3(DateTime dt)
{
return TToC3(dt, true);
}
+ public static string TToC3(DateTime dt, IGxContext context)
+ {
+ return TToC3(dt, true, context);
+ }
internal const string JsonDateFormatMillis = "yyyy-MM-ddTHH:mm:ss.fff";
internal const string JsonDateFormat = "yyyy-MM-ddTHH:mm:ss";
-
+
+ //[Obsolete("TToC3 is deprecated, use TToC3(DateTime, bool, IGxContext) instead", false)]
public static string TToC3(DateTime dt, bool toUTC)
{
- return TToCRest(dt, "0000-00-00T00:00:00.000", JsonDateFormatMillis, toUTC);
+ return TToCRest(dt, "0000-00-00T00:00:00.000", JsonDateFormatMillis, null, toUTC);
+ }
+ internal static string TToC3(DateTime dt, bool toUTC, IGxContext context)
+ {
+ return TToCRest(dt, "0000-00-00T00:00:00.000", JsonDateFormatMillis, context, toUTC);
}
- static string TToCRest(DateTime dt, String nullString, String formatStr, bool toUTC=true)
+ static string TToCRest(DateTime dt, String nullString, String formatStr, IGxContext context, bool toUTC=true)
{
if (dt == nullDate)
return FormatEmptyDate(nullString);
else
{
- DateTime ret = Preferences.useTimezoneFix() ? (toUTC ? toUniversalTime(dt) : dt) : dt;
+ DateTime ret;
+ if (context != null)
+ {
+ ret = Preferences.useTimezoneFix() ? (toUTC ? toUniversalTime(dt, context) : dt) : dt;
+ }
+ else
+ {
+ ret = Preferences.useTimezoneFix() ? (toUTC ? toUniversalTime(dt) : dt) : dt;
+ }
return ret.ToString(formatStr, CultureInfo.InvariantCulture);
}
}
@@ -3536,7 +3757,7 @@ static private bool isNullDateCompatible(DateTime dt)
else
return isNullDate(dt);
}
-
+ //[Obsolete("fromUniversalTime is deprecated, use fromUniversalTime(DateTime, IGxContext) instead", false)]
static public DateTime fromUniversalTime(DateTime dt)
{
#if NODATIME
@@ -3545,7 +3766,15 @@ static public DateTime fromUniversalTime(DateTime dt)
return isNullDateCompatible(dt) ? dt : fromUniversalTime(dt, GxContext.Current.GetOlsonTimeZone());
#endif
}
-
+ static public DateTime fromUniversalTime(DateTime dt, IGxContext context)
+ {
+#if NODATIME
+ return isNullDateCompatible(dt) ? dt : fromUniversalTime(dt, context.GetTimeZone());
+#else
+ return isNullDateCompatible(dt) ? dt : fromUniversalTime(dt, context.GetOlsonTimeZone());
+#endif
+ }
+ //[Obsolete("toUniversalTime is deprecated, use toUniversalTime(DateTime, IGxContext) instead", false)]
static public DateTime toUniversalTime(DateTime dt)
{
#if NODATIME
@@ -5455,15 +5684,22 @@ public static void ErrorToMessages(string errorId, string errorDescription, GXBa
}
}
public static void ErrorToMessages(string errorId, Exception ex, GXBaseCollection Messages)
+ {
+ ErrorToMessages(errorId, ex, Messages, true);
+ }
+ internal static void ErrorToMessages(string errorId, Exception ex, GXBaseCollection Messages, bool parseInnerExceptions)
{
if (Messages != null && ex != null)
{
StringBuilder str = new StringBuilder();
str.Append(ex.Message);
- while (ex.InnerException != null)
+ if (parseInnerExceptions)
{
- str.Append(ex.InnerException.Message);
- ex = ex.InnerException;
+ while (ex.InnerException != null)
+ {
+ str.Append(ex.InnerException.Message);
+ ex = ex.InnerException;
+ }
}
ErrorToMessages(errorId, str.ToString(), Messages);
}
@@ -5551,7 +5787,7 @@ public string IniReadValue(string Section, string Key)
#endif
public static class GXDbFile
{
- static readonly IGXLogger log = GXLoggerFactory.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName);
+ static readonly IGXLogger log = GXLoggerFactory.GetLogger(typeof(GXDbFile).FullName);
private static Regex schemeRegex = new Regex("^" + Scheme + ":", RegexOptions.Compiled);
@@ -5772,7 +6008,7 @@ public static string PathToUrl(string path, bool absUrl, IGxContext context = nu
public static class GxImageUtil
{
- static readonly IGXLogger log = GXLoggerFactory.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName);
+ static readonly IGXLogger log = GXLoggerFactory.GetLogger(typeof(GxImageUtil).FullName);
private static Bitmap BitmapCreateFromStream(string filePathOrUrl)
{
Uri uri;
diff --git a/dotnet/src/dotnetframework/GxClasses/Core/Web/HttpAjaxContext.cs b/dotnet/src/dotnetframework/GxClasses/Core/Web/HttpAjaxContext.cs
index 4eebfe44d..b78405faa 100644
--- a/dotnet/src/dotnetframework/GxClasses/Core/Web/HttpAjaxContext.cs
+++ b/dotnet/src/dotnetframework/GxClasses/Core/Web/HttpAjaxContext.cs
@@ -1,7 +1,10 @@
namespace GeneXus.Http
{
using System;
+ using System.IO;
+#if !NETCORE
using Jayrock.Json;
+#endif
using System.Web;
using GeneXus.Application;
using GeneXus.Utils;
@@ -41,12 +44,6 @@ public interface IHttpAjaxContext
void AddStylesHidden();
void ajax_rsp_clear();
- JArray AttValues { get;}
- JObject HiddenValues { get;}
- JArray PropValues { get; }
- JObject WebComponents { get;}
- JObject Messages { get;}
- JArray Grids { get; }
void LoadFormVars(HttpContext localHttpContext);
void disableJsOutput();
void enableJsOutput();
@@ -88,7 +85,7 @@ public void ClearParmsMetadata()
public bool isInputParm(string key)
{
- return inParmsMetadataHash.Contains(key);
+ return (inParmsMetadataHash!=null && inParmsMetadataHash.Contains(key));
}
public void Clear()
@@ -108,7 +105,7 @@ public bool isParmModified(string fieldName, object value)
IGxJSONSerializable jsonValue = value as IGxJSONSerializable;
if (jsonValue!=null)
{
- if (!inParmsHashValue.ContainsKey(fieldName))
+ if (inParmsHashValue!=null && !inParmsHashValue.ContainsKey(fieldName))
return true;
return GXUtil.GetHash(jsonValue.ToJSonString()) != inParmsHashValue[fieldName];
}
@@ -146,24 +143,24 @@ public class HttpAjaxContext : IHttpAjaxContext
public string FormCaption { get; set; }
- public JObject HiddenValues
+ internal JObject HiddenValues
{ get { return _HiddenValues; } }
- public JArray AttValues
+ internal JArray AttValues
{ get { return _AttValues; } }
- public JArray PropValues
+ internal JArray PropValues
{ get { return _PropValues; } }
- public JObject WebComponents
+ internal JObject WebComponents
{ get { return _WebComponents; } }
- public Hashtable LoadCommands
+ internal Hashtable LoadCommands
{ get { return _LoadCommands; } }
- public JObject Messages
+ internal JObject Messages
{ get {
return _Messages;
}
}
- public JArray Grids
+ internal JArray Grids
{ get { return _Grids; } }
- public JObject ComponentObjects
+ internal JObject ComponentObjects
{ get { return _ComponentObjects; } }
public GXAjaxCommandCollection Commands
{ get { return commands; } }
@@ -210,7 +207,7 @@ public void appendAjaxCommand(String cmdType, Object cmdData)
commands.AppendCommand(new GXAjaxCommand(cmdType, cmdData));
}
- public void appendLoadData(int SId, JObject Data)
+ internal void appendLoadData(int SId, JObject Data)
{
LoadCommands[SId] = Data;
}
@@ -349,7 +346,7 @@ public void ajax_rsp_assign_sdt_attri(String CmpContext, bool IsMasterPage, Stri
try
{
JObject obj = GetGxObject(AttValues, CmpContext, IsMasterPage);
- if (obj != null && (DynAjaxEventContext.isParmModified(AttName, SdtObj) || !isUndefinedOutParam( AttName, SdtObj)))
+ if (obj != null && (!isUndefinedOutParam(AttName, SdtObj) || DynAjaxEventContext.isParmModified(AttName, SdtObj)))
{
IGxJSONAble SdtObjJson = SdtObj as IGxJSONAble;
if (SdtObjJson != null)
@@ -667,7 +664,7 @@ internal string getJSONResponse(string cmpContext)
return jsonCmdWrapper.ToString();
}
- public static JArray GetParmsJArray(Object[] parms)
+ internal static JArray GetParmsJArray(Object[] parms)
{
JArray inputs = new JArray();
for (int i = 0; i < parms.Length; i++)
@@ -688,29 +685,36 @@ public static JArray GetParmsJArray(Object[] parms)
public string GetAjaxEncryptionKey()
{
- if (context.ReadSessionKey(CryptoImpl.AJAX_ENCRYPTION_KEY) == null)
+ string ajaxKey = context.ReadSessionKey(CryptoImpl.AJAX_ENCRYPTION_KEY);
+ if (ajaxKey == null)
{
- if(!RecoverEncryptionKey())
- context.WriteSessionKey(CryptoImpl.AJAX_ENCRYPTION_KEY,CryptoImpl.GetRijndaelKey());
+ string sessionKey;
+ if (!RecoverEncryptionKey(out sessionKey)) {
+ ajaxKey = CryptoImpl.GetRijndaelKey();
+ context.WriteSessionKey(CryptoImpl.AJAX_ENCRYPTION_KEY, ajaxKey);
+ }
+ else
+ {
+ ajaxKey = sessionKey;
+ }
}
- return context.ReadSessionKey(CryptoImpl.AJAX_ENCRYPTION_KEY);
+ return ajaxKey;
}
- private bool RecoverEncryptionKey()
+ private bool RecoverEncryptionKey(out string sessionKey)
{
- if ( (context.ReadSessionKey(CryptoImpl.AJAX_ENCRYPTION_KEY) == null))
+ sessionKey = null;
+ if (context.HttpContext != null)
{
- if (context.HttpContext != null)
+ String clientKey = context.HttpContext.Request.Headers[CryptoImpl.AJAX_SECURITY_TOKEN];
+ if (!string.IsNullOrEmpty(clientKey))
{
- String clientKey = context.HttpContext.Request.Headers[CryptoImpl.AJAX_SECURITY_TOKEN];
- if (!string.IsNullOrEmpty(clientKey))
+ bool correctKey;
+ clientKey = CryptoImpl.DecryptRijndael(CryptoImpl.GX_AJAX_PRIVATE_IV + clientKey, CryptoImpl.GX_AJAX_PRIVATE_KEY, out correctKey);
+ if (correctKey)
{
- bool correctKey = false;
- clientKey = CryptoImpl.DecryptRijndael(CryptoImpl.GX_AJAX_PRIVATE_IV + clientKey, CryptoImpl.GX_AJAX_PRIVATE_KEY, out correctKey);
- if (correctKey)
- {
- context.WriteSessionKey(CryptoImpl.AJAX_ENCRYPTION_KEY, clientKey);
- return true;
- }
+ sessionKey = clientKey;
+ context.WriteSessionKey(CryptoImpl.AJAX_ENCRYPTION_KEY, clientKey);
+ return true;
}
}
}
@@ -744,7 +748,7 @@ public void LoadFormVars(HttpContext localHttpContext)
}
}
- public void ParseGXState(JObject tokenValues)
+ internal void ParseGXState(JObject tokenValues)
{
if (tokenValues != null)
{
@@ -821,7 +825,7 @@ public string Content
}
}
- public class GXJObject : JObject
+ internal class GXJObject : JObject
{
private bool base64Encoded;
@@ -882,7 +886,7 @@ public object Data
}
}
- public JObject JSONObject
+ internal JObject JSONObject
{
get
{
@@ -1027,7 +1031,7 @@ private GXAjaxCommand GetCommand(GXAjaxCommand cmd)
return null;
}
- public JArray JSONArray
+ internal JArray JSONArray
{
get
{
diff --git a/dotnet/src/dotnetframework/GxClasses/Core/gxconfig.cs b/dotnet/src/dotnetframework/GxClasses/Core/gxconfig.cs
index 6a3789eaf..5dadc513c 100644
--- a/dotnet/src/dotnetframework/GxClasses/Core/gxconfig.cs
+++ b/dotnet/src/dotnetframework/GxClasses/Core/gxconfig.cs
@@ -157,7 +157,25 @@ public static bool GetValueOf(string sId, out string sString)
return false;
}
}
-
+ internal static bool GetValueOrEnvironmentVarOf(string sId, out string sString)
+ {
+ try
+ {
+ sString = config.Get(sId);
+ if (String.IsNullOrEmpty(sString))
+ {
+ sString = MappedValue(sId, sString);
+ if (string.IsNullOrEmpty(sString))
+ return false;
+ }
+ return true;
+ }
+ catch
+ {
+ sString = string.Empty;
+ return false;
+ }
+ }
public static bool GetValueOf(string sId)
{
string sString;
@@ -533,14 +551,15 @@ static NameValueCollection config
{
loadedConfigFile = configFileName;
_config = loadConfig(configFileName, out logConfigSource);
+ configLoaded = true;
if (!string.IsNullOrEmpty(logConfigSource))
logConfig(logConfigSource, out configuredFilename);
else
logConfig(configFileName, out configuredFilename);
}
+#if !NETCORE
else
{
-#if !NETCORE
if (GxContext.IsHttpContext &&
File.Exists(Path.Combine(GxContext.StaticPhysicalPath(), "web.config")))
{
@@ -576,10 +595,13 @@ static NameValueCollection config
logConfig(logFile, out configuredFilename);
loadedConfigFile = Path.GetFullPath(file);
_config = loadConfig(file);
-
}
-
+ configLoaded = true;
+ }
#else
+ else
+ {
+
string basePath = FileUtil.GetBasePath();
string currentDir = Directory.GetCurrentDirectory();
string startupDir = FileUtil.GetStartupDirectory();
@@ -635,8 +657,8 @@ static NameValueCollection config
{
//"Could not register encoding provider";
}
-#endif
}
+#endif
}
}
log = GXLoggerFactory.GetLogger();
@@ -826,6 +848,15 @@ public class Preferences
const string DEFAULT_DS = "Default";
static int httpclient_max_per_route = -1;
static int sessionTimeout = -1;
+ static int singletonHttpClient = -1;
+ internal static string AppMainNamespace
+ {
+ get
+ {
+ Config.GetValueOf("AppMainNamespace", out string nameSpace);
+ return nameSpace;
+ }
+ }
internal static string DefaultDatastore
{
get
@@ -1413,6 +1444,18 @@ internal static bool WrapSingleApiOutput
return DefaultWrapSingleApiOutput;
}
}
+
+ internal static bool SingletonHttpClient()
+ {
+ if (singletonHttpClient == -1)
+ {
+ if (Config.GetValueOrEnvironmentVarOf("HTTPCLIENT_SINGLETON", out string sValue) && int.TryParse(sValue, out int value))
+ singletonHttpClient = value;
+ else
+ singletonHttpClient = 1;
+ }
+ return singletonHttpClient==1;
+ }
internal static string CorsAllowedOrigins()
{
if (Config.GetValueOf("CORS_ALLOW_ORIGIN", out string corsOrigin))
@@ -1454,27 +1497,27 @@ public static string GetDefaultTheme()
else
return "";
}
-
- public static int GetHttpClientMaxConnectionPerRoute()
+ internal const int DEFAULT_HTTPCLIENT_MAX_PER_ROUTE= 1000;
+ internal static int GetHttpClientMaxConnectionPerRoute()
{
if (httpclient_max_per_route == -1)
{
try
{
string strmax;
- if (Config.GetValueOf("HTTPCLIENT_MAX_PER_ROUTE", out strmax))
+ if (Config.GetValueOrEnvironmentVarOf("HTTPCLIENT_MAX_PER_ROUTE", out strmax))
{
httpclient_max_per_route = Convert.ToInt32(strmax);
}
else
{
- httpclient_max_per_route = 1000;
+ httpclient_max_per_route = DEFAULT_HTTPCLIENT_MAX_PER_ROUTE;
}
}
catch (Exception ex)
{
GXLogging.Error(log, "HttpClientMaxPerRoute error", ex);
- httpclient_max_per_route = 1000;
+ httpclient_max_per_route = DEFAULT_HTTPCLIENT_MAX_PER_ROUTE;
}
}
return httpclient_max_per_route;
diff --git a/dotnet/src/dotnetframework/GxClasses/Data/GXDataADO.cs b/dotnet/src/dotnetframework/GxClasses/Data/GXDataADO.cs
index 22938b4ad..7cfe94720 100644
--- a/dotnet/src/dotnetframework/GxClasses/Data/GXDataADO.cs
+++ b/dotnet/src/dotnetframework/GxClasses/Data/GXDataADO.cs
@@ -8,6 +8,7 @@
using System.IO;
using System.Reflection;
using System.Threading;
+using System.Threading.Tasks;
using GeneXus.Application;
using GeneXus.Cache;
using GeneXus.Configuration;
@@ -212,7 +213,22 @@ public void RemoveAllConnections(int handle)
throw e;
}
}
+#if NETCORE
+ internal async Task RemoveConnectionAsync(int handle, string dataSource)
+ {
+ ServerUserInformation sui;
+ if (userConnections.TryGetValue(handle, out sui))
+ {
+ GXLogging.Debug(log, "RemoveConnection handle " + handle + ",datasource:" + dataSource);
+ GxConnection con = sui[dataSource];
+ if (sui.TryRemove(dataSource, out con))
+ await con.DisposeAsync();
+ ServerUserInformation suiDeleted;
+ if (sui.Count == 0) userConnections.TryRemove(handle, out suiDeleted);
+ }
+ }
+#endif
public void RemoveConnection(int handle, string dataSource)
{
@@ -411,7 +427,12 @@ public void Dispose()
{
Close();
}
-
+#if NETCORE
+ internal async Task DisposeAsync()
+ {
+ await CloseAsync();
+ }
+#endif
public IGxDataStore DataStore
{
get{ return dataStore;}
@@ -736,7 +757,61 @@ public void Close()
wmiconnection.CleanUp();
}
}
+#if NETCORE
+ internal async Task CloseAsync()
+ {
+ if (connection != null)
+ {
+ GXLogging.Debug(log, "GxConnection.Close Id " + " connection State '" + connection.State + "'" + " handle:" + handle + " datastore:" + DataStore.Id);
+ }
+ if (connection != null && ((connection.State & ConnectionState.Closed) == 0))
+ {
+ try
+ {
+ connectionCache.Clear();
+ }
+ catch (Exception e)
+ {
+ GXLogging.Warn(log, "GxConnection.Close can't close all prepared cursors", e);
+ }
+ GXLogging.Debug(log, "UncommitedChanges before Close:" + UncommitedChanges);
+ try
+ {
+ if (UncommitedChanges)
+ {
+ rollbackTransactionOnly();
+ UncommitedChanges = false;
+ }
+ }
+ catch (Exception e)
+ {
+ GXLogging.Warn(log, "GxConnection.Close can't rollback transaction", e);
+ }
+ try
+ {
+ await connection.CloseAsync();
+ if (transaction != null)
+ {
+ transaction.Dispose();
+ transaction = null;
+ }
+
+ }
+ catch (Exception e)
+ {
+ GXLogging.Warn(log, "GxConnection.Close can't close connection", e);
+ }
+ spid = 0;
+ GXLogging.Debug(log, "GxConnection.Close connection is closed ");
+ }
+ m_opened = false;
+ if (Preferences.Instrumented && wmiconnection != null)
+ {
+ wmiconnection.CleanUp();
+ }
+ }
+#endif
public int OpenHandles
{
get{return openHandles;}
@@ -2719,7 +2794,12 @@ GxDataRecord getDbmsDataRecord(string id, string dbms)
switch (dbms)
{
case "sqlserver":
- return new GxSqlServer();
+ GxSqlServer gxSqlServer = new GxSqlServer();
+ if (Config.GetValueOf("Connection-" + id + "-UseSmallDateTime", out cfgBuf) && cfgBuf == "1")
+ {
+ gxSqlServer.UseSmallDateTime();
+ }
+ return gxSqlServer;
case "mysql":
#if NETCORE
return new GxMySqlConnector(id);
@@ -2837,6 +2917,12 @@ public void CloseConnections()
{
GxConnectionManager.Instance.RemoveConnection(handle, id);
}
+#if NETCORE
+ internal async Task CloseConnectionsAsync()
+ {
+ await ((GxConnectionManager)GxConnectionManager.Instance).RemoveConnectionAsync(handle, id);
+ }
+#endif
public void Release()
{
}
diff --git a/dotnet/src/dotnetframework/GxClasses/Data/GXDataCommon.cs b/dotnet/src/dotnetframework/GxClasses/Data/GXDataCommon.cs
index 362374b08..1eb63d2f7 100644
--- a/dotnet/src/dotnetframework/GxClasses/Data/GXDataCommon.cs
+++ b/dotnet/src/dotnetframework/GxClasses/Data/GXDataCommon.cs
@@ -25,6 +25,7 @@
using GeneXus.Metadata;
using System.Data.Common;
using System.Linq;
+using System.Threading.Tasks;
namespace GeneXus.Data
{
@@ -1599,7 +1600,12 @@ public class GxSqlServer : GxDataRecord
private const string INTEGRATED_SECURITY_NO = "no";
#endif
private bool multipleDatareadersEnabled;
+ private DateTime SqlServer_NullDateTime= SQLSERVER_NULL_DATETIME;
+ internal void UseSmallDateTime()
+ {
+ SqlServer_NullDateTime = SQLSERVER_NULL_SMALLDATETIME;
+ }
public override int GetCommandTimeout()
{
return base.GetCommandTimeout();
@@ -2114,14 +2120,14 @@ public override DateTime Dbms2NetDate(IGxDbCommand cmd, IDataRecord DR, int i)
public override DateTime Dbms2NetDateTime( DateTime dt, Boolean precision)
{
//DBMS MinDate => Genexus null Date
- if (dt.Equals(SQLSERVER_NULL_DATE))
+ if (dt.Equals(SqlServer_NullDateTime))
{
return DateTimeUtil.NullDate();
}
- if (dt.Year==SQLSERVER_NULL_DATE.Year &&
- dt.Month==SQLSERVER_NULL_DATE.Month &&
- dt.Day==SQLSERVER_NULL_DATE.Day)
+ if (dt.Year== SqlServer_NullDateTime.Year &&
+ dt.Month== SqlServer_NullDateTime.Month &&
+ dt.Day== SqlServer_NullDateTime.Day)
{
return new DateTime(
@@ -2137,16 +2143,16 @@ public override Object Net2DbmsDateTime(IDbDataParameter parm, DateTime dt)
//Genexus null => save DBMS MinDate
if(dt.Equals(DateTimeUtil.NullDate()))
{
- return SQLSERVER_NULL_DATE;
+ return SqlServer_NullDateTime;
}
//Date < DBMS MinDate => save DBMS MinDate keeping the Time component
- if (dt.CompareTo(SQLSERVER_NULL_DATE)<0)
+ if (dt.CompareTo(SqlServer_NullDateTime) <0)
{
DateTime aux =
new DateTime(
- SQLSERVER_NULL_DATE.Year,SQLSERVER_NULL_DATE.Month,
- SQLSERVER_NULL_DATE.Day,dt.Hour,dt.Minute,dt.Second,dt.Millisecond);
+ SqlServer_NullDateTime.Year, SqlServer_NullDateTime.Month,
+ SqlServer_NullDateTime.Day,dt.Hour,dt.Minute,dt.Second,dt.Millisecond);
return aux;
}
@@ -2161,8 +2167,8 @@ public override string ConcatOp(int pos)
{
return ConcatOpValues[pos];
}
-
- static DateTime SQLSERVER_NULL_DATE = new DateTime(1753,1,1) ;
+ static DateTime SQLSERVER_NULL_DATETIME = new DateTime(1753,1,1) ;
+ static DateTime SQLSERVER_NULL_SMALLDATETIME= new DateTime(1900, 1, 1);
}
public class GxDataReader: IDataReader
@@ -2174,7 +2180,7 @@ public class GxDataReader: IDataReader
protected string stmt;
protected GxConnectionCache cache;
protected IGxConnection con;
- protected GxArrayList block;
+ protected List