protected override void ProcessQueueEntries(List <ExceptionWrapper> entryCollection) { try { DateTime?lastFailedToLogToStoreDate = null; foreach (var ew in entryCollection) { try { AddExceptionToDB(ew); } catch (Exception ee) { // prevent logging failures too often if (!lastFailedToLogToStoreDate.HasValue || DateTime.Now.Subtract(lastFailedToLogToStoreDate.Value).TotalSeconds >= 25) { Log.Error(ee, $"Failed to log exception to DB store. EP={ew.EndpointKey}; sID={ew.sId};"); Log.Error($"Original: {ew.message}; stack=\r\n{ew.stackTrace}"); lastFailedToLogToStoreDate = DateTime.Now; } } } } catch (Exception ex) { Log.Error(ex, "ExceptionLogger::ProcessMessagesLoop failed"); SessionLog.Error("ExceptionLogger::ProcessMessagesLoop failed"); SessionLog.Exception(ex); } }
public void Init() { try { if (File.Exists(InlinePluginManifestPath)) { var json = File.ReadAllText(InlinePluginManifestPath); if (!string.IsNullOrWhiteSpace(json)) { var entries = JsonConvert.DeserializeObject <InlineModuleManifestEntry[]>(json); _entries = new List <InlineModuleManifestEntry>(entries); Entries = _entries.AsReadOnly(); } else { _entries = new List <InlineModuleManifestEntry>(); Entries = _entries.AsReadOnly(); } } else { _entries = new List <InlineModuleManifestEntry>(); Entries = _entries.AsReadOnly(); } } catch (Exception ex) { ExceptionLogger.LogException(ex); SessionLog.Exception(ex); } }
public static bool ValidateUrlAcl(bool isHttps, string hostname, int port) { try { var url = $"http{(isHttps ? "s" : "")}://{hostname}:{port}".ToLower(); var acl = ShowUrlAcl(isHttps, hostname, port); var lines = acl.Split("\r\n", StringSplitOptions.RemoveEmptyEntries); var l = lines.FirstOrDefault(ln => ln.ToLower().Contains("reserved url")); if (l != null) { return(l.ToLower().Contains(url)); } else { return(false); } } catch (Exception ex) { SessionLog.Exception(ex); return(false); } }
public CommonReturnValue GetInlinePluginModuleSource(string inlineEntryId, out InlineModuleManifestEntry existingInlineEntry, out string source) { source = null; existingInlineEntry = null; var pluginAssembly = this.PluginAssemblies.FirstOrDefault(p => p.InlineEntryId != null && p.InlineEntryId.Equals(inlineEntryId, StringComparison.Ordinal) && p.IsInline); if (pluginAssembly == null) { return(CommonReturnValue.UserError($"An inline module with the Id '{inlineEntryId}' does not exist")); } try { existingInlineEntry = InlineModuleManifest.Instance.GetEntryById(pluginAssembly.InlineEntryId); var sourcePath = System.IO.Path.Combine(InlinePluginSourcePath, existingInlineEntry.Id); if (System.IO.File.Exists(sourcePath)) { source = System.IO.File.ReadAllText(sourcePath); return(CommonReturnValue.Success()); } else { return(CommonReturnValue.UserError($"Failed to find source at: {sourcePath}")); } } catch (Exception e) { SessionLog.Warning("Failed to fetch file of plugin module with InstanceId = {0}; {1}", pluginAssembly.InstanceId, pluginAssembly.Assembly.FullName); SessionLog.Exception(e); } return(CommonReturnValue.Success()); }
public static bool ValidateSSLCertBinding(string hostname, int port) { try { var output = ShowSslCert(hostname, port); if (output == null) { return(false); } output = output.ToLower(); if (output.Contains("the system cannot find the file specified")) { return(false); } return(true); } catch (Exception ex) { SessionLog.Exception(ex); return(false); } }
public static ApiResponseServerMethodBase Exception(Exception ex) { SessionLog.Exception(ex); // TODO: Record ServerMethod detail var id = ExceptionLogger.LogException(ex, (Controllers.ExecController.ExecOptions)null); var ret = new ApiResponseServerMethodBase(); ret.Error = $"Error ref: {id}"; ret.Type = ApiResponseType.Exception; return(ret); }
public static void Start() { try { var endpoints = SettingsInstance.Instance.ProjectList.SelectMany(p => p.Applications).SelectMany(app => app.Endpoints).ToList(); WorkSpawner.TEMPLATE_RoutineContainer = File.ReadAllText("./resources/RoutineContainerTemplate.txt"); WorkSpawner.TEMPLATE_Routine = File.ReadAllText("./resources/RoutineTemplate.txt"); WorkSpawner.TEMPLATE_TypescriptDefinitions = File.ReadAllText("./resources/TypeScriptDefinitionsContainer.d.ts"); WorkSpawner._workerList = new List <Worker>(); //dbSources = new DatabaseSource[] { dbSources.First() }.ToList(); //TEMP // TODO: handle items (project/sources) that were deleted //async.each(dbSources, (source) => { endpoints.ForEach(endpoint => { //TEST!! // if (endpoint.Name != "DEV" || endpoint.Application.Name != "PWAs") return; try { CreateNewWorker(endpoint); } catch (Exception e) { Log.Error(e, $"Failed to create new work for {endpoint.Pedigree}"); ExceptionLogger.LogException(e); } }); Hubs.WorkerMonitor.Instance.NotifyObservers(); } catch (Exception e) { SessionLog.Exception(e); Log.Error(e, "Work spawner error on start"); } } // Start
public void AddPlugin(PluginInfo plugin) { try { this._plugins.Add(plugin); if (plugin.Type == PluginType.BackgroundThread) { BackgroundThreadPluginManager.Instance.Register(plugin); } else if (plugin.Type == PluginType.ServerMethod) { ServerMethodManager.Register(InstanceId, plugin); } } catch (Exception ex) { SessionLog.Exception(ex); } }
public override void Init() { try { _throttleTracker = new Dictionary <string, List <DateTime> >(); _database = new LiteDB.LiteDatabase("data/exceptions.db"); var exceptionCollection = _database.GetCollection <ExceptionWrapper>("Exceptions"); exceptionCollection.EnsureIndex("sId", unique: true); exceptionCollection.EnsureIndex("EndpointKey", unique: false); base.Init(); } catch (Exception ex) { SessionLog.Exception(ex); Log.Error(ex, "Failed to initiate Exceptions DB"); } }
public static void RemoveEndpoint(Endpoint endpoint) { try { lock (_workerList) { var worker = GetWorkerByEndpoint(endpoint); if (worker != null) { worker.Stop(); _workerList.Remove(worker); Hubs.WorkerMonitor.Instance.NotifyObservers(); } } } catch (Exception ex) { SessionLog.Exception(ex); } }
private static PluginSetParameterValue GetParameterValueFromPlugins(CachedRoutine routine, string parameterName, List <ExecutionPlugin> plugins) { foreach (var plugin in plugins) { try { var val = plugin.GetParameterValue(routine.Schema, routine.Routine, parameterName); if (val != PluginSetParameterValue.DontSet) { return(val); } } catch (Exception ex) { SessionLog.Error("Plugin {0} GetParameterValue failed", plugin.Name); SessionLog.Exception(ex); } } return(PluginSetParameterValue.DontSet); }
private (List <ResultSetFieldMetadata>, string) ExtractResultSetMetadata(string resultSetXml) { try { var xmlSerializer = new System.Xml.Serialization.XmlSerializer(typeof(SqlResultSetFieldCollection)); resultSetXml = $"<FieldCollection>{resultSetXml}</FieldCollection>"; using (var sr = new StringReader(resultSetXml)) { var val = (xmlSerializer.Deserialize(sr) as Settings.ObjectModel.SqlResultSetFieldCollection); // look for error if (val.Fields.Count > 0 && !string.IsNullOrWhiteSpace(val.Fields[0].ErrorMsg)) { var f = val.Fields[0]; return(null, $"({f.ErrorDescription}) {f.ErrorMsg}"); } else { return(val.Fields.Select(f => new ResultSetFieldMetadata() { ColumnName = f.Name, DataType = RoutineParameterV2.GetCSharpDataTypeFromSqlDbType(f.Type), DbDataType = f.Type, ColumnSize = f.Size, NumericalPrecision = f.Precision, NumericalScale = f.Scale }).ToList(), null); } } } catch (Exception ex) { SessionLog.Exception(ex); return(null, "Failed to parse ResultSetXml:" + ex.Message); } }
private void CompileCodeIntoAssemblyContext(InlineModuleManifestEntry inlineEntry, string code) { var ctxName = $"Inline Plugin Context {++_asmCtxCounter}"; var pluginPath = Path.GetFullPath("plugins"); var asmCtx = new PluginAssemblyLoadContext(pluginPath, ctxName, true /*enable unloading*/); ASM_CTXES.Add(asmCtx); SessionLog.Info($"Created {ctxName} for {inlineEntry.Name}".PadRight(35)); var assemblyBytes = CSharpCompilerHelper.CompileIntoAssembly(inlineEntry.Name, code, out var problems); if ((problems != null && problems.Count == 0)) { Assembly assembly = null; try { using (var ms = new MemoryStream(assemblyBytes)) { assembly = asmCtx.LoadFromStream(ms); ParseAndLoadPluginAssembly(asmCtx, assembly, inlineEntry.Id); } } catch (Exception ee) { SessionLog.Error($"Failed to load inline plugin assembly '{assembly?.FullName}' {inlineEntry.Name}/{inlineEntry.Id}. See exception that follows."); SessionLog.Exception(ee); } } else { SessionLog.Error($"Inline plugin {inlineEntry.Name} ({inlineEntry.Id}) failed to compile with the following error(s): {string.Join(", ", problems)}"); } }
public static IWebHostBuilder BuildWebHost(string pathToContentRoot, string[] args) { var webServerSettings = SettingsInstance.Instance.Settings.WebServer; // var certPath = "cert.pfx"; // var certPassPath = Path.GetFullPath("cert.pass"); // if (!File.Exists(certPassPath)) // { // throw new Exception($"Unable to find cert path file: {certPassPath}"); // } // var certPass = System.IO.File.ReadAllText(certPassPath); // var configurationBuilder = new ConfigurationBuilder(); // configurationBuilder.AddJsonFile("./appsettings.json", false, true); // var appConfig = configurationBuilder.Build(); return(WebHost .CreateDefaultBuilder(args) .UseSerilog() .UseContentRoot(pathToContentRoot) .UseWebRoot(Path.Combine(pathToContentRoot, "wwwroot")) .UseSetting(WebHostDefaults.SuppressStatusMessagesKey, "true") // .ConfigureAppConfiguration((builderContext, config) => // { // IHostingEnvironment env = builderContext.HostingEnvironment; // config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true); // //.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true); // }) .UseHttpSys(options => { options.Authentication.Schemes = AuthenticationSchemes.None; options.Authentication.AllowAnonymous = true; options.MaxConnections = null; options.MaxRequestBodySize = 30000000; //~30MB int interfaceCnt = 0; if ((webServerSettings.EnableSSL ?? false) && !string.IsNullOrWhiteSpace(webServerSettings.HttpsServerHostname) && webServerSettings.HttpsServerPort.HasValue && !string.IsNullOrWhiteSpace(webServerSettings.HttpsCertHash)) { try { var httpsUrl = $"https://{webServerSettings.HttpsServerHostname}:{webServerSettings.HttpsServerPort.Value}"; if (NetshWrapper.ValidateSSLCertBinding(webServerSettings.HttpsServerHostname, webServerSettings.HttpsServerPort.Value)) { if (NetshWrapper.ValidateUrlAcl(true, webServerSettings.HttpsServerHostname, webServerSettings.HttpsServerPort.Value)) { //!eventLog.Info($"Listening to {httpsUrl}"); options.UrlPrefixes.Add(httpsUrl); interfaceCnt++; } else { if (NetshWrapper.AddUrlToACL(true, webServerSettings.HttpsServerHostname, webServerSettings.HttpsServerPort.Value)) { //!eventLog.Info($"Listening to {httpsUrl}"); options.UrlPrefixes.Add(httpsUrl); interfaceCnt++; } else { SessionLog.Error($"The url '{httpsUrl}' was not found in ACL list so a listener for this URL cannot be started."); Log.Error($"The url '{httpsUrl}' was not found in ACL list so a listener for this URL cannot be started."); } } } else { SessionLog.Error($"There is no SSL cert binding for '{httpsUrl}' so a listener for this URL cannot be started."); Log.Error($"There is no SSL cert binding for '{httpsUrl}' so a listener for this URL cannot be started."); } } catch (Exception ex) { SessionLog.Exception(ex); Log.Error(ex, "HTTPS init failed"); } } if (webServerSettings.EnableBasicHttp ?? false) { try { var httpUrl = $"http://{webServerSettings.HttpServerHostname}:{webServerSettings.HttpServerPort}"; if (NetshWrapper.ValidateUrlAcl(false, webServerSettings.HttpServerHostname, webServerSettings.HttpServerPort.Value)) { //!eventLog.Info($"Listening to {httpUrl}"); options.UrlPrefixes.Add(httpUrl); interfaceCnt++; } else { if (NetshWrapper.AddUrlToACL(false, webServerSettings.HttpServerHostname, webServerSettings.HttpServerPort.Value)) { //!eventLog.Info($"Listening to {httpUrl}"); options.UrlPrefixes.Add(httpUrl); interfaceCnt++; } else { SessionLog.Error($"The url '{httpUrl}' was not found in ACL list so a listener for this URL cannot be started."); Log.Error($"The url '{httpUrl}' was not found in ACL list so a listener for this URL cannot be started."); } } } catch (Exception ex) { SessionLog.Exception(ex); Log.Error(ex, "Basic Http init failed"); } } if (interfaceCnt == 0) { Log.Warning("No valid interface (http or https) found so defaulting to localhost:9086"); options.UrlPrefixes.Add("http://localhost:9086"); } }) // .UseKestrel(options => // { // options.AddServerHeader = false; // // TODO: Allow more config here, especially the limits // //!options.Limits.MaxConcurrentConnections // if (webServerSettings.EnableSSL ?? false) // { // if (File.Exists(certPath)) // { // options.Listen(System.Net.IPAddress.Any, webServerSettings.HttpsServerPort ?? 44312, listenOptions => // { // try // { // listenOptions.UseHttps(certPath, certPass); // } // catch (System.Exception ex) // { // SessionLog.Exception(ex); // Cons ole.WriteLine(ex.ToString()); // throw; // } // }); // } // else // { // Cons ole.WriteLine("Cannot start HTTPS listener: The cert file '{0}' does not exists.", certPath); // } // } // if (webServerSettings.EnableBasicHttp ?? false) // { // options.Listen(System.Net.IPAddress.Any, webServerSettings.HttpServerPort ?? 9086); // http // } // }) .UseStartup <Startup>() .UseSetting(WebHostDefaults.DetailedErrorsKey, "true") ); // .UseUrls("http://localhost:9086", "https://*:4430") }
public static void GenerateJsFileV2(string source, Endpoint endpoint, JsFile jsFile, Dictionary <string, ChangeDescriptor> fullChangeSet = null, bool rulesChanged = false) { var generateMetric = new Performance.ExecutionBase("GenerateJsFileV2"); var noChanges = false; try { // TODO: Figure out out casing on this property string jsNamespace = null;//endpoint.JsNamespace; if (string.IsNullOrWhiteSpace(jsNamespace)) { jsNamespace = endpoint.MetadataConnection.InitialCatalog; } var jsSafeNamespace = MakeNameJsSafe(jsNamespace); var routineContainerTemplate = WorkSpawner.TEMPLATE_RoutineContainer; var routineTemplate = WorkSpawner.TEMPLATE_Routine; var typescriptDefinitionsContainer = WorkSpawner.TEMPLATE_TypescriptDefinitions; endpoint.ApplyRules(jsFile); var includedRoutines = (from row in endpoint.CachedRoutines where !row.IsDeleted && (row.RuleInstructions[jsFile]?.Included ?? false == true) orderby row.FullName select row).ToList(); List <KeyValuePair <string, ChangeDescriptor> > changesInFile = null; if (fullChangeSet != null) { changesInFile = fullChangeSet.Where(change => includedRoutines.Count(inc => inc.FullName.Equals(change.Key, StringComparison.OrdinalIgnoreCase)) > 0).ToList(); // TODO: Consider if this is a good idea? // If we can reasonably say that there are no changes to routines that this JsFile cares about, why regenerate this file and why give it a new Version if (changesInFile.Count == 0) { noChanges = true; return; } } var jsSchemaLookupForJsFunctions = new Dictionary <string, List <string> /*Routine defs*/>(); var tsSchemaLookup = new Dictionary <string, List <string> /*Routine defs*/>(); var typeScriptParameterAndResultTypesSB = new StringBuilder(); var serverMethodPlugins = PluginLoader.Instance.PluginAssemblies .SelectMany(pa => pa.Plugins) .Where(p => p.Type == PluginType.ServerMethod && endpoint.Application.IsPluginIncluded(p.Guid.ToString())); var uniqueSchemas = new List <string>(); var mainLoopMetric = generateMetric.BeginChildStage("Main loop"); includedRoutines.ForEach(r => { try { if (r.TypescriptMethodStub == null) { r.PrecalculateJsGenerationValues(endpoint); } var jsSchemaName = JsFileGenerator.MakeNameJsSafe(r.Schema); var jsFunctionName = JsFileGenerator.MakeNameJsSafe(r.Routine); if (!jsSchemaLookupForJsFunctions.ContainsKey(jsSchemaName)) { jsSchemaLookupForJsFunctions.Add(jsSchemaName, new List <string>()); } if (!tsSchemaLookup.ContainsKey(jsSchemaName)) { tsSchemaLookup.Add(jsSchemaName, new List <string>()); } if (!uniqueSchemas.Contains(r.Schema)) { uniqueSchemas.Add(r.Schema); } var schemaIx = uniqueSchemas.IndexOf(r.Schema); // .js { var jsFunctionDefLine = routineTemplate.Replace("<<FUNC_NAME>>", jsFunctionName).Replace("<<SCHEMA_IX>>", schemaIx.ToString()).Replace("<<ROUTINE>>", r.Routine); if (r.Type.Equals(string.Intern("PROCEDURE"), StringComparison.OrdinalIgnoreCase)) { jsFunctionDefLine = jsFunctionDefLine.Replace("<<CLASS>>", "S"); } else { jsFunctionDefLine = jsFunctionDefLine.Replace("<<CLASS>>", "U"); } jsSchemaLookupForJsFunctions[jsSchemaName].Add(jsFunctionDefLine); } // .tsd { typeScriptParameterAndResultTypesSB.AppendLine(r.TypescriptParameterTypeDefinition); typeScriptParameterAndResultTypesSB.AppendLine(r.TypescriptOutputParameterTypeDefinition); typeScriptParameterAndResultTypesSB.AppendLine(r.TypescriptResultSetDefinitions); tsSchemaLookup[jsSchemaName].Add(r.TypescriptMethodStub); } } catch (Exception ex) { SessionLog.Exception(ex); // TODO: quit whole process } }); mainLoopMetric.End(); var finalSBMetric = generateMetric.BeginChildStage("Final SB"); var schemaAndRoutineDefs = string.Join("\r\n", jsSchemaLookupForJsFunctions.Select(s => "\tx." + s.Key + " = {\r\n\t\t" + string.Join(",\r\n\t\t", s.Value.ToArray()) + "\r\n\t}\r\n").ToArray()); var tsSchemaAndRoutineDefs = string.Join("\r\n", tsSchemaLookup.Select(s => "\t\tclass " + s.Key + " {\r\n" + string.Join(";\r\n", s.Value.ToArray()) + "\r\n\t\t}\r\n").ToArray()); var finalSB = new StringBuilder(routineContainerTemplate); jsFile.IncrementVersion(); // record changes against new version if (changesInFile != null && changesInFile.Count > 0) { JsFileChangesTracker.Instance.AddUpdate(endpoint, jsFile, changesInFile.Select(kv => kv.Value).ToList()); } if (rulesChanged) { JsFileChangesTracker.Instance.AddUpdate(endpoint, jsFile, new List <ChangeDescriptor> { ChangeDescriptor.Create("System", "One or more rules changed.") }); } finalSB.Replace("<<DATE>>", DateTime.Now.ToString("dd MMM yyyy, HH:mm")) .Replace("<<FILE_VERSION>>", jsFile.Version.ToString()) .Replace("<<SERVER_NAME>>", Environment.MachineName) .Replace("<<ENDPOINT>>", endpoint.Pedigree) .Replace("<<UNIQUE_SCHEMAS>>", string.Join(',', uniqueSchemas.Select(k => $"'{k}'"))) .Replace("<<Catalog>>", jsSafeNamespace) .Replace("<<ROUTINES>>", schemaAndRoutineDefs) ; var finalTypeScriptSB = new StringBuilder(); finalTypeScriptSB = finalTypeScriptSB.Append(typescriptDefinitionsContainer); // Custom/User types if (endpoint.CustomTypeLookupWithTypeScriptDef.Count > 0) { var customTSD = from kv in endpoint.CustomTypeLookupWithTypeScriptDef select $"\t\ttype {kv.Key} = {kv.Value};"; typeScriptParameterAndResultTypesSB.Insert(0, string.Join("\r\n", customTSD)); } var resultAndParameterTypes = typeScriptParameterAndResultTypesSB.ToString(); finalTypeScriptSB.Replace("<<DATE>>", DateTime.Now.ToString("dd MMM yyyy, HH:mm")) .Replace("<<FILE_VERSION>>", jsFile.Version.ToString()) .Replace("<<SERVER_NAME>>", Environment.MachineName) .Replace("<<Catalog>>", jsSafeNamespace) .Replace("<<ResultAndParameterTypes>>", resultAndParameterTypes) .Replace("<<MethodsStubs>>", tsSchemaAndRoutineDefs) ; finalSBMetric.End(); var toStringMetric = generateMetric.BeginChildStage("ToString"); var typescriptDefinitionsOutput = finalTypeScriptSB.ToString(); var finalOutput = finalSB.ToString(); toStringMetric.End(); var filePath = endpoint.OutputFilePath(jsFile); var minfiedFilePath = endpoint.MinifiedOutputFilePath(jsFile); var tsTypingsFilePath = endpoint.OutputTypeScriptTypingsFilePath(jsFile); var minifyMetric = generateMetric.BeginChildStage("Minify"); var minifiedSource = Uglify.Js(finalOutput /*, { }*/).Code; minifyMetric.End(); if (!Directory.Exists(endpoint.OutputDir)) { Directory.CreateDirectory(endpoint.OutputDir); } var fileOutputMetric = generateMetric.BeginChildStage("Write"); var jsFinalBytes = System.Text.Encoding.UTF8.GetBytes(finalOutput); var jsFinalMinifiedBytes = System.Text.Encoding.UTF8.GetBytes(minifiedSource); jsFile.ETag = Controllers.PublicController.ComputeETag(jsFinalBytes); jsFile.ETagMinified = Controllers.PublicController.ComputeETag(jsFinalMinifiedBytes); File.WriteAllText(filePath, finalOutput); File.WriteAllText(minfiedFilePath, minifiedSource); File.WriteAllText(tsTypingsFilePath, typescriptDefinitionsOutput); fileOutputMetric.End(); } finally { generateMetric.End(); SessionLog.InfoToFileOnly($"{endpoint.Pedigree.PadRight(25, ' ')} - {generateMetric.DurationInMS.ToString().PadLeft(4)} ms {jsFile.Filename.PadRight(20)} (source={source};rulesChanged={rulesChanged};changes={!noChanges}); {generateMetric.ChildDurationsSingleLine()}"); } }
// generates and caches the ServerMethod .js and .tsd for a specific ServerMethod Plugin Registration private void GenerateAndCacheJsInterface() { try { var definitionsJS = JavaScriptDefinitions = new Dictionary <string, List <Definition> >(); var definitionsTSD = TypescriptDefinitions = new Dictionary <string, List <Definition> >(); JavaScriptDefinitionsHash = TypescriptDefinitionsHash = null; ConverterLookup = new List <string>(); var namespaceLookup = new List <string>(); // add default namespace definitionsJS.Add("ServerMethods", new List <Definition>()); definitionsTSD.Add("ServerMethods", new List <Definition>()); foreach (var method in this.Registration.Methods) { var namespaceKey = "ServerMethods"; var namespaceKeyTSD = "ServerMethods"; var jsNamespaceVar = Strings.@null; // null for main ServerMethod namespace if (!string.IsNullOrEmpty(method.Namespace)) { namespaceKey = method.Namespace; namespaceKeyTSD = method.Namespace; } var isCustomNamespace = !namespaceKey.Equals("ServerMethods", StringComparison.Ordinal); if (isCustomNamespace) { if (!namespaceLookup.Contains(namespaceKey)) { namespaceLookup.Add(namespaceKey); } jsNamespaceVar = $"_ns[{namespaceLookup.IndexOf(namespaceKey)}]"; } if (!definitionsJS.ContainsKey(namespaceKey)) { definitionsJS.Add(namespaceKey, new List <Definition>()); } if (!definitionsTSD.ContainsKey(namespaceKeyTSD)) { definitionsTSD.Add(namespaceKeyTSD, new List <Definition>()); } var methodParameters = method.AssemblyMethodInfo.GetParameters(); var inputConvertersLookup = new Dictionary <string, ConverterDefinition>(); var outputConvertersLookup = new Dictionary <string, ConverterDefinition>(); var resultsConvertersLookup = new Dictionary <string, ConverterDefinition>(); foreach (var inputParam in methodParameters) { GlobalConverterLookup.AnalyseForRequiredOutputConverters(inputParam.Name, inputParam.ParameterType, null, ref inputConvertersLookup); } foreach (var outputParam in methodParameters.Where(p => p.IsOut || p.ParameterType.IsByRef)) { GlobalConverterLookup.AnalyseForRequiredOutputConverters(outputParam.Name, outputParam.ParameterType, null, ref outputConvertersLookup); } GlobalConverterLookup.AnalyseForRequiredOutputConverters("$result$", method.AssemblyMethodInfo.ReturnType, null, ref resultsConvertersLookup); string inputConverter = null; string outputConverter = null; string resultConverter = null; var allRequiredConverters = inputConvertersLookup.Select(c => c.Value.ToJson()).Distinct() .Concat(outputConvertersLookup.Select(c => c.Value.ToJson()).Distinct()) .Concat(resultsConvertersLookup.Select(c => c.Value.ToJson()).Distinct()) .ToList(); if (allRequiredConverters.Count > 0) { foreach (var converterJson in allRequiredConverters) { if (ConverterLookup.IndexOf(converterJson) == -1) { ConverterLookup.Add(converterJson); } } } inputConverter = string.Join(",", (from kv in inputConvertersLookup select $"\"{kv.Key}\": $c[{kv.Value.ToJson()}]")); // ignore ConverterOptions for now as we don't actually have any use for it at the moment outputConverter = string.Join(",", (from kv in outputConvertersLookup select $"\"{kv.Key}\": $c[{kv.Value.ToJson()}]")); // ignore ConverterOptions for now as we don't actually have any use for it at the moment resultConverter = string.Join(",", (from kv in resultsConvertersLookup select $"\"{kv.Key}\": $c[{kv.Value.ToJson()}]")); // ignore ConverterOptions for now as we don't actually have any use for it at the moment inputConverter = string.IsNullOrWhiteSpace(inputConverter) ? null : inputConverter; outputConverter = string.IsNullOrWhiteSpace(outputConverter) ? null : outputConverter; resultConverter = string.IsNullOrWhiteSpace(resultConverter) ? null : resultConverter; var hasConverters = inputConverter != null || outputConverter != null || resultConverter != null; // js var methodLineJS = ServerMethodManager.TEMPLATE_ServerMethodFunctionTemplate .Replace("<<FUNC_NAME>>", method.Name) .Replace("<<NAMESPACE>>", jsNamespaceVar); ; if (!hasConverters) { methodLineJS = methodLineJS.Replace("<<CONV_SEP>>", ""); methodLineJS = methodLineJS.Replace("<<CONVERTERS>>", ""); } if (methodParameters.Count() > 0) { methodLineJS = methodLineJS.Replace("<<ARG_SEP>>", ", "); methodLineJS = methodLineJS.Replace("<<HAS_PARMS>>", "o"); } else { methodLineJS = methodLineJS.Replace("<<ARG_SEP>>", ""); methodLineJS = methodLineJS.Replace("<<HAS_PARMS>>", ""); } definitionsJS[namespaceKey].Add(new Definition() { MethodName = method.Name, Line = methodLineJS, InputConverter = inputConverter, OutputConverter = outputConverter, ResultsConverter = resultConverter }); // TSD string methodLineTSD = null; if (isCustomNamespace) { methodLineTSD = SERVER_TSD_METHOD_NONSTATIC_TEMPLATE.Replace("<<FUNC_NAME>>", method.Name); } else { methodLineTSD = SERVER_TSD_METHOD_TEMPLATE.Replace("<<FUNC_NAME>>", method.Name); } var inputParmListLookup = from p in methodParameters select new { Name = p.Name, p.IsOut, p.ParameterType.IsByRef, p.ParameterType.IsArray, p.ParameterType.IsValueType, HasDefault = p.IsOptional, IsNullable = Nullable.GetUnderlyingType(p.ParameterType) != null, TypescriptDataType = GlobalTypescriptTypeLookup.GetTypescriptTypeFromCSharp(p.ParameterType) }; var inputParmsFormatted = from p in inputParmListLookup select $"{p.Name}{((p.HasDefault) ? "?" : "")}: {p.TypescriptDataType}"; // TODO: Revise. Not clear if IsNullable should also be output with a '?'. In TypeScript this means optional and not 'nullable'. So in C# even if a parameter is nullable it is still required to specified it. ? should be reserved for OPTIONAL parameters //select $"{p.Name}{((p.HasDefault || p.IsNullable ) ? "?" : "")}: {p.TypescriptDataType}"; string typeNameBase = $"{(isCustomNamespace ? namespaceKeyTSD + "_" : "")}{ method.Name }"; string typeNameInputParms = $"{typeNameBase}_In"; string typeNameOutputParms = $"{typeNameBase}_Out"; string typeNameResult = $"{typeNameBase}_Res"; string typeDefInputParms = null; string typeDefOutputParms = null; string typeDefResult = null; // if there are INPUT parameters if (inputParmsFormatted.Count() > 0) { methodLineTSD = methodLineTSD.Replace("<<PARM_LIST>>", "parameters?: __." + typeNameInputParms); typeDefInputParms = $"type {typeNameInputParms} = {"{" + string.Join(", ", inputParmsFormatted) + "}"};"; typeNameInputParms = "__." + typeNameInputParms; } else { methodLineTSD = methodLineTSD.Replace("<<PARM_LIST>>", ""); typeNameInputParms = "void"; } // if there are OUTPUT parameters if (inputParmListLookup.Count(p => p.IsOut) > 0) { typeDefOutputParms = $"type {typeNameOutputParms} = {{ " + string.Join(", ", (from p in inputParmListLookup where p.IsOut select $"{p.Name}: {p.TypescriptDataType}")) + " };"; typeNameOutputParms = "__." + typeNameOutputParms; } else { typeNameOutputParms = "void"; } if (method.AssemblyMethodInfo.ReturnType == typeof(void)) // no result { //IServerMethodVoid<OuputParameters, InputParameters> methodLineTSD = methodLineTSD.Replace("<<RET_TYPE>>", $"IServerMethodVoid<{typeNameOutputParms}, {typeNameInputParms}>"); } else { //IServerMethod<OuputParameters, ResultType, InputParameters> var retType = GlobalTypescriptTypeLookup.GetTypescriptTypeFromCSharp(method.AssemblyMethodInfo.ReturnType); // if a built-in js/TS type if (new string[] { "number", "string", "date", "boolean", "any", "number[]", "string[]", "date[]", "boolean[]", "any[]" }.Contains(retType.ToLower())) { typeDefResult = null; methodLineTSD = methodLineTSD.Replace("<<RET_TYPE>>", $"IServerMethod<{typeNameOutputParms}, {retType}, {typeNameInputParms}>"); } else { typeDefResult = $"type {typeNameResult} = {retType};"; methodLineTSD = methodLineTSD.Replace("<<RET_TYPE>>", $"IServerMethod<{typeNameOutputParms}, __.{typeNameResult}, {typeNameInputParms}>"); } } definitionsTSD[namespaceKeyTSD].Add(new Definition() { MethodName = method.Name, Line = methodLineTSD, TypesLines = new List <string>() { typeDefInputParms, typeDefOutputParms, typeDefResult } }); } // foreach (var method in this.Methods) var jsLines = string.Join("\n", definitionsJS.Select(kv => $"{kv.Key}§{string.Join('\n', kv.Value.Select(d => d.Line).ToArray())}").ToArray()); var tsdLines = string.Join("\n", definitionsTSD.Select(kv => $"{kv.Key}§{string.Join('\n', kv.Value.Select(d => d.Line).ToArray())}").ToArray()); using (var sha = System.Security.Cryptography.SHA256.Create()) { var jsHash = sha.ComputeHash(System.Text.Encoding.UTF8.GetBytes(jsLines)); var tsdHash = sha.ComputeHash(System.Text.Encoding.UTF8.GetBytes(tsdLines)); // TODO: Compare with current Hashes. If different then let all relevant Apps know this.JavaScriptDefinitionsHash = jsHash; this.TypescriptDefinitionsHash = tsdHash; } } catch (Exception ex) { SessionLog.Exception(ex); } }
// public static async Task<(bool, List<string>)> Evaluate(string code) // { // try // { // var options = ScriptOptions.Default.WithReferences(GetCommonMetadataReferences()); // await CSharpScript.EvaluateAsync(code, options, globals: null, globalsType: null, cancellationToken: CancellationToken.None); // return (true, null); // } // catch (CompilationErrorException ce) // { // return (false, (from d in ce.Diagnostics // select d.ToString()).ToList()); // } // } // public static async Task<(bool/*isValid*/, List<string>/*problems*/, List<BasePluginRuntime>/*parsedPluginCollection*/)> EvalAndParseAsync(string id, string code) // { // bool isValid; // List<string> problems = null; // List<BasePluginRuntime> parsedPluginCollection = null; // (isValid, problems) = await CSharpCompilerHelper.Evaluate(code); // if (isValid) // if compilation went well // { // isValid = CSharpCompilerHelper.ParseForPluginTypes(id, code, out parsedPluginCollection, out problems); // } // return (isValid, problems, parsedPluginCollection); // } public static byte[] CompileIntoAssembly(string assemblyName, string source, out List <string> problems) { problems = new List <string>(); try { if (string.IsNullOrEmpty(assemblyName)) { problems.Add("Assembly name must have a value"); } if (string.IsNullOrEmpty(source)) { problems.Add("No source provided"); } if (problems.Count > 0) { return(null); } CSharpParseOptions parseOptions = CSharpParseOptions.Default; SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(source, options: parseOptions); // if (Debugger.IsAttached) // { // pluginBasePath = "./../jsdal-plugin/bin/Debug/netcoreapp2.0/jsdal-plugin.dll"; // } //var pluginBaseRef = MetadataReference.CreateFromFile(pluginBasePath); //var trustedAssembliesPaths = ((string)AppContext.GetData("TRUSTED_PLATFORM_ASSEMBLIES")).Split(Path.PathSeparator); MetadataReference[] references = GetCommonMetadataReferences(); var compilationOptions = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary) .WithUsings(new string[] { "System" }); CSharpCompilation compilation = CSharpCompilation.Create( assemblyName, syntaxTrees: new[] { syntaxTree }, references: references, options: compilationOptions ); using (var ms = new MemoryStream()) { EmitResult result = compilation.Emit(ms); if (!result.Success) { IEnumerable <Diagnostic> failures = result.Diagnostics.Where(diagnostic => diagnostic.IsWarningAsError || diagnostic.Severity == DiagnosticSeverity.Error); problems = failures.Select(f => f.GetMessage()).ToList(); foreach (Diagnostic diagnostic in failures) { Console.Error.WriteLine("{0}: {1}", diagnostic.Id, diagnostic.GetMessage()); } return(null); } else { ms.Seek(0, SeekOrigin.Begin); //?AssemblyLoadContext.Default.LoadFromStream //Assembly assembly = Assembly.Load(ms.ToArray()); //?var assembly = asmCtx.LoadFromStream(ms); return(ms.ToArray()); //ParseAndLoadPluginAssembly(assembly); } } } catch (Exception ex) { problems.Add(ex.ToString()); SessionLog.Exception(ex); return(null); } }
//AssemblyLoadContext, AssemblyName, Assembly? // private Assembly OnResolveAssembly(AssemblyLoadContext ctx, AssemblyName asmName) // { // try // { // // try and find relative to existing assebmlies loaded in ctx // foreach (var asm in ctx.Assemblies) // { // var fi = new FileInfo(asm.Location); // var path = Path.Combine(fi.DirectoryName, asmName.Name + ".dll"); // if (File.Exists(path)) // { // try // { // var loadedAsm = ctx.LoadFromAssemblyPath(path); // if (loadedAsm != null) return loadedAsm; // } // catch { } // } // } // SessionLog.Error($"Failed to resolve {asmName.FullName} in context {ctx.Name}"); // return null; // } // catch // { // return null; // } // } // private Assembly OnResolveAssembly(object sender, ResolveEventArgs e) // { // try // { // var asmName = new AssemblyName(e.Name); // var requestingLocation = e.RequestingAssembly.Location; // var requestingDir = Path.GetDirectoryName(requestingLocation); // // look for a dll in the same location as the requesting assembly // var path = Path.Combine(requestingDir, asmName.Name + ".dll"); // if (!File.Exists(path)) return null; // Assembly.LoadFrom(path); // return null; // } // catch // { // return null; // } // } // private void InitServerWidePlugins() // { // try // { // if (PluginAssemblies == null) return; // foreach (var pluginAssembly in PluginAssemblies) // { // var pluginInfoCollection = pluginAssembly.Plugins.Where(p => p.Type == OM.PluginType.ServerMethod || p.Type == OM.PluginType.BackgroundThread); // foreach (var pluginInfo in pluginInfoCollection) // { // if (pluginInfo.Type == OM.PluginType.BackgroundThread) // { // _backgroundThreadManager.Register(pluginInfo); // } // else if (pluginInfo.Type == OM.PluginType.ServerMethod) // { // ServerMethodManager.Register(pluginAssembly.InstanceId, pluginInfo); // } // } // } // } // catch (Exception ex) // { // SessionLog.Exception(ex); // } // } // parses an Assembly and checks for Plugin-type interface. If found each of those interfaces are tested for validity in terms of mandatory Attribute values and uniqueness private bool ParsePluginAssembly(Assembly pluginAssembly, out List <PluginInfo> pluginInfoList, out List <string> errorList, bool checkForConflict = true) { errorList = new List <string>(); pluginInfoList = new List <PluginInfo>(); if (pluginAssembly.DefinedTypes != null) { var pluginTypeList = pluginAssembly.DefinedTypes.Where(typ => typ.IsSubclassOf(typeof(PluginBase))).ToList(); if (pluginTypeList != null && pluginTypeList.Count > 0) { foreach (var pluginType in pluginTypeList) { var pluginInfo = new PluginInfo(); try { var pluginData = pluginType.GetCustomAttribute(typeof(PluginDataAttribute)) as PluginDataAttribute; if (pluginData == null) { errorList.Add($"Plugin '{pluginType.FullName}' from assembly '{pluginAssembly.FullName}' does not have a PluginData attribute defined on the class level. Add a jsdal_plugin.PluginDataAttribute to the class."); continue; } if (!Guid.TryParse(pluginData.Guid, out var pluginGuid)) { errorList.Add($"Plugin '{pluginType.FullName}' does not have a valid Guid value set on its PluginData attribute."); continue; } if (checkForConflict) { var conflict = PluginAssemblies.SelectMany(a => a.Plugins).FirstOrDefault(p => p.Guid.Equals(pluginGuid)); if (conflict != null) { errorList.Add($"Plugin '{pluginType.FullName}' has a conflicting Guid. The conflict is on assembly {conflict.TypeInfo.FullName} and plugin '{conflict.Name}' with Guid value {conflict.Guid}."); continue; } } if (pluginType.IsSubclassOf(typeof(ExecutionPlugin))) { pluginInfo.Type = OM.PluginType.Execution; } else if (pluginType.IsSubclassOf(typeof(BackgroundThreadPlugin))) { pluginInfo.Type = OM.PluginType.BackgroundThread; } else if (pluginType.IsSubclassOf(typeof(ServerMethodPlugin))) { pluginInfo.Type = OM.PluginType.ServerMethod; // TODO: Additional validation: Look for at least on ServerMethod? otherwise just a warning? // What about unique names of ServerMethods? // Validate Custom Namespace validity (must be JavaScript safe) } else { errorList.Add($"Unknown plugin type '{pluginType.FullName}'."); continue; } pluginInfo.Assembly = pluginAssembly; pluginInfo.Name = pluginData.Name; pluginInfo.Description = pluginData.Description; pluginInfo.TypeInfo = pluginType; pluginInfo.Guid = pluginGuid; //errorList.Add($"Plugin '{pluginInfo.Name}' ({pluginInfo.Guid}) loaded. Assembly: {pluginAssembly.FullName}"); pluginInfoList.Add(pluginInfo); } catch (Exception ex) { errorList.Add("Failed to instantiate type '{pluginType.FullName}'."); errorList.Add(ex.ToString()); SessionLog.Error("Failed to instantiate type '{0}'. See the exception that follows.", pluginType.FullName); SessionLog.Exception(ex); } } } else { errorList.Add($"Failed to find any jsDAL Server plugins in the assembly '{pluginAssembly.Location}'. Make sure you have a public class available that derives from one of the plugin types."); } } else { errorList.Add($"Failed to find any jsDAL Server plugins in the assembly '{pluginAssembly.Location}'. Make sure you have a public class available that derives from one of the plugin types."); } return(errorList == null || errorList.Count == 0); }
public static void Main(string[] args) { IsShuttingDown = false; var loggerConfig = new LoggerConfiguration() .MinimumLevel.Debug() .MinimumLevel.Override("Microsoft", LogEventLevel.Warning) .MinimumLevel.Override("Microsoft.AspNetCore", LogEventLevel.Warning) .Enrich.FromLogContext() .WriteTo.File("log/detail-.txt", //?restrictedToMinimumLevel: LogEventLevel.Warning, rollingInterval: RollingInterval.Day, retainedFileCountLimit: 7, shared: true, outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss} {Level:u3} {Message:lj}{NewLine}{Exception}" ) // .WriteTo.File("log/info-.txt", // rollingInterval: RollingInterval.Day, // shared: true, // outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss} {Level:u3} {Message:lj}{NewLine}{Exception}", // restrictedToMinimumLevel: LogEventLevel.Information // ) ; try { CTS = new CancellationTokenSource(); var isService = args.Length == 1 && args[0].Equals("--service", StringComparison.OrdinalIgnoreCase); var justRun = args.Length == 1 && args[0].Equals("--run", StringComparison.OrdinalIgnoreCase); if (isService) { var pathToExe = Process.GetCurrentProcess().MainModule.FileName; var pathToContentRootService = Path.GetDirectoryName(pathToExe); Directory.SetCurrentDirectory(pathToContentRootService); } else if (!Debugger.IsAttached && !justRun) { TerminalUI.Init(); return; } else { loggerConfig.WriteTo.Console(); } Log.Logger = loggerConfig.CreateLogger(); var pathToContentRoot = Directory.GetCurrentDirectory(); if (Debugger.IsAttached) { // var pathToExe = Process.GetCurrentProcess().MainModule.FileName; // pathToContentRoot = Path.GetDirectoryName(pathToExe); } else { // now relying on serilog // OverrideStdout(); } AppDomain.CurrentDomain.UnhandledException += (sender, e) => { var isTerminating = e.IsTerminating; Log.Fatal("Unhandled exception", e.ExceptionObject); ExceptionLogger.LogException(e.ExceptionObject as Exception); }; AppConfigSettings appConfigSettings = null; try { var appSettingsJson = File.ReadAllText("./appsettings.json"); appConfigSettings = System.Text.Json.JsonSerializer.Deserialize <AppConfigSettings>(appSettingsJson); } catch (Exception ex) { Log.Error(ex, "Failed to read appsettings.json"); } _startDate = DateTime.Now; var _ = UptimeLogger.LogServerUptimeAsync(); Log.Information($"Application started with process id {System.Diagnostics.Process.GetCurrentProcess().Id}."); // I forget what the different msg types look like Log.Warning("This is what a warning looks like"); Log.Error("This is what an error looks like"); Log.Fatal("This is what a fatal error looks like"); Log.Information("Loading users"); UserManagement.LoadUsersFromFile(); Log.Information("Initialising exception logger"); ExceptionLogger.Instance.Init(); Log.Information("Loading settings"); SettingsInstance.LoadSettingsFromFile(); Log.Information("Initialising real-time tracker"); RealtimeTrackerThread.Instance.Init(); if (appConfigSettings?.AppSettings?.Startup?.HealthMonitor ?? false) { Log.Information("Initialising jsDAL health monitor"); jsDALHealthMonitorThread.Instance.Init(); } else { Log.Information("jsDAL health monitor not configured to run at startup"); } if (appConfigSettings?.AppSettings?.Startup?.DataCollector ?? false) { Log.Information("Initialising data collector"); DataCollectorThread.Instance.Init(); } else { Log.Information("Data collector not configured to run at startup"); } Log.Information("Initialising inline module manifest"); InlineModuleManifest.Instance.Init(); Log.Information("Configuring global culture"); var globalCulture = new System.Globalization.CultureInfo("en-US"); // set global culture to en-US - will help with things like parsing numbers from Javascript(e.g. 10.123) as double/decimal even if server uses a comma as decimal separator for example CultureInfo.DefaultThreadCurrentCulture = globalCulture; CultureInfo.DefaultThreadCurrentUICulture = globalCulture; Log.Information("Building web host"); var builder = BuildWebHost(pathToContentRoot, args); var host = builder.Build(); if (isService) { host.RunAsCustomService(); } else { host.RunAsync(); Console.WriteLine("\r\nPress CTRL+X to shutdown server"); while (true) { var keyInfo = Console.ReadKey(true); if (keyInfo.Key == ConsoleKey.X && (keyInfo.Modifiers & ConsoleModifiers.Control) == ConsoleModifiers.Control) { //host.StopAsync(); ShutdownAllBackgroundThreads(); break; } } } } catch (Exception ex) { Log.Fatal(ex, "Application terminated unexpectedly"); SessionLog.Exception(ex); ShutdownAllBackgroundThreads(); } finally { Log.CloseAndFlush(); } }
public async Task InitAsync() { // load from plugin directory try { // AppDomain.CurrentDomain.AssemblyResolve += (sender, e) => // { // try // { // var asmName = new AssemblyName(e.Name); // var requestingLocation = e.RequestingAssembly.Location; // var requestingDir = Path.GetDirectoryName(requestingLocation); // // look for a dll in the same location as the requesting assembly // var path = Path.Combine(requestingDir, asmName.Name + ".dll"); // if (!File.Exists(path)) return null; // Assembly.LoadFrom(path); // return null; // } // catch // { // return null; // } // }; var pluginPath = Path.GetFullPath("plugins"); if (Directory.Exists("./plugins")) { var dllCollection = Directory.EnumerateFiles("plugins", "*.dll", SearchOption.TopDirectoryOnly); foreach (var dllPath in dllCollection) { // skip jsdal-plugin base if (dllPath.Equals("plugins\\jsdal-plugin.dll", StringComparison.OrdinalIgnoreCase)) { continue; } try { var ctxName = $"Plugin Context {++_asmCtxCounter}"; var asmCtx = new PluginAssemblyLoadContext(pluginPath, ctxName, true /*enable unloading*/); ASM_CTXES.Add(asmCtx); SessionLog.Info($"Created {ctxName} for {dllPath}".PadRight(35)); var dllFullPath = Path.GetFullPath(dllPath); var pluginAssembly = asmCtx.LoadFromAssemblyPath(dllFullPath); ParseAndLoadPluginAssembly(asmCtx, pluginAssembly, null); } catch (Exception ee) { SessionLog.Error("Failed to load plugin DLL '{0}'. See exception that follows.", dllPath); SessionLog.Exception(ee); } } } } catch (Exception ex) { SessionLog.Exception(ex); } // load inline assemblies try { foreach (var inlineEntry in InlineModuleManifest.Instance.Entries) { var sourcePath = Path.Combine(InlinePluginSourcePath, inlineEntry.Id); if (File.Exists(sourcePath)) { var code = await File.ReadAllTextAsync(sourcePath); CompileCodeIntoAssemblyContext(inlineEntry, code); } else { SessionLog.Error($"Inline module {inlineEntry.Name} not found at '{sourcePath}'"); } } } catch (Exception ex) { SessionLog.Exception(ex); } // init server-wide types // try // { // InitServerWidePlugins(); // } // catch (Exception ex) // { // SessionLog.Exception(ex); // } }