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);
        }
Esempio n. 2
0
        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
Esempio n. 3
0
        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);
            }
        }
Esempio n. 4
0
        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);
            }
        }
        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");
            }
        }
Esempio n. 6
0
        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);
        }
Esempio n. 7
0
        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);
            }
        }
Esempio n. 8
0
        private void ParseAndLoadPluginAssembly(AssemblyLoadContext asmCtx, Assembly assembly, string inlineEntryId = null)
        {
            if (ParsePluginAssembly(assembly, out var pluginInfoList, out var errorList, checkForConflict: true))
            {
                foreach (var pluginInfo in pluginInfoList)
                {
                    SessionLog.Info($"{(inlineEntryId != null ? "(Inline) " : "")}Plugin '{pluginInfo.Name}' ({pluginInfo.Guid}) found in assembly: {assembly.FullName}");

                    var existing = PluginAssemblies.FirstOrDefault(a => a.Assembly == assembly);

                    if (existing == null)
                    {
                        var newPA = new PluginAssembly(asmCtx, assembly, inlineEntryId);
                        newPA.AddPlugin(pluginInfo);
                        _pluginAssemblies.Add(newPA);
                    }
                    else
                    {
                        existing.AddPlugin(pluginInfo);
                    }
                }
            }
        }
Esempio n. 9
0
        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)}");
            }
        }
Esempio n. 10
0
        public void Run()
        {
            try
            {
                Thread.CurrentThread.Name = "WorkerThread " + this.Endpoint.Pedigree;

                this.Status = "Started";

                this.IsRunning          = true;
                this.IsOutputFilesDirty = false;

                //DateTime lastSavedDate = DateTime.Now;

                var cache = this.Endpoint.CachedRoutines;

                if (cache != null && cache.Count > 0)
                {
                    this.MaxRowDate = cache.Max(c => c.RowVer);
                }

                int connectionOpenErrorCnt = 0;

                if (this.Endpoint?.MetadataConnection?.ConnectionStringDecrypted == null)
                {
                    this.IsRunning = false;
                    this.Status    = $"Endpoint '{this.Endpoint?.Pedigree ?? "(null)"}' does not have a valid metadata connection configured.";
                    this.log.Error(this.Status);
                    SessionLog.Error(this.Status);
                    return;
                }

                var exceptionThrottler = new SortedList <DateTime, Exception>();

                while (this.IsRunning)
                {
                    // look for new instructions first
                    lock (_instructionQueue)
                    {
                        while (_instructionQueue.Count > 0)
                        {
                            var ins = _instructionQueue.Dequeue();

                            if (ins.Type == WorkerInstructionType.RegenAllFiles)
                            {
                                this.ForceGenerateAllOutputFiles(this.Endpoint);
                            }
                            else if (ins.Type == WorkerInstructionType.RegenSpecificFile)
                            {
                                this.ForceGenerateOutputFile(this.Endpoint, ins.JsFile);
                            }
                        }
                    }

                    isIterationDirty = false;

                    string connectionStringRefForLog = null;

                    try
                    {
                        if (!string.IsNullOrEmpty(Endpoint.PullMetadataFromEndpointId))
                        {
                            var ep = Settings.SettingsInstance.Instance.FindEndpointById(Endpoint.PullMetadataFromEndpointId);

                            if (ep != null)
                            {
                                this.Status = $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm")} - Metadata pulled from {ep.Pedigree}";
                            }
                            else
                            {
                                this.Status = $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm")} - ERROR. Metadata configured to pull from endpoint with Id {Endpoint.PullMetadataFromEndpointId} but source endpoint not found.";
                            }

                            this.IsRunning = false;
                            continue;
                        }
                        else if (Endpoint.DisableMetadataCapturing)
                        {
                            this.Status    = $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm")} - Metadata capturing disabled";
                            this.IsRunning = false;
                            continue;
                        }
                        else if (!Endpoint.IsOrmInstalled)
                        {
                            // try again in 3 seconds
                            this.Status = $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm")} - Waiting for ORM to be installed";
                            Thread.Sleep(3000);
                            continue;
                        }

                        var csb = new SqlConnectionStringBuilder(this.Endpoint.MetadataConnection.ConnectionStringDecrypted);

                        connectionStringRefForLog = $"Data Source={csb.DataSource}; UserId={csb.UserID}; Catalog={csb.InitialCatalog}";

                        var appName = $"jsdal-server worker {this.Endpoint.Pedigree} {System.Environment.MachineName}";

                        csb.ApplicationName = appName.Left(128);

                        var connectionStringDecrypted = csb.ToString();

                        using (var con = new SqlConnection(connectionStringDecrypted))
                        {
                            try
                            {
                                con.Open();
                                connectionOpenErrorCnt = 0;
                            }
                            catch (Exception oex)
                            {
                                this.Status = "Failed to open connection to database: " + oex.Message;
                                this.log.Exception(oex, connectionStringRefForLog);
                                connectionOpenErrorCnt++;

                                int waitMS = Math.Min(3000 + (connectionOpenErrorCnt * 3000), 300000 /*Max 5mins between tries*/);

                                this.Status = $"Attempt: #{connectionOpenErrorCnt + 1} (waiting for {waitMS / 1000} secs). " + this.Status;

                                Hubs.WorkerMonitor.Instance.NotifyObservers();

                                Thread.Sleep(waitMS);
                                continue;
                            }

                            ProcessAsync(con, connectionStringDecrypted).Wait();
                        } // using connection

                        if (isIterationDirty)
                        {
                            Hubs.WorkerMonitor.Instance.NotifyObservers();
                        }

                        Thread.Sleep(SettingsInstance.Instance.Settings.DbSource_CheckForChangesInMilliseconds);
                    }
                    catch (Exception ex)
                    {
                        this.log.Exception(ex);

                        exceptionThrottler.Add(DateTime.Now, ex);

                        var thresholdDate = DateTime.Now.AddSeconds(-60);

                        var beforeThreshold = exceptionThrottler.Where(kv => kv.Key < thresholdDate).ToList();

                        // remove items outside of threshold checking window
                        for (int i = 0; i < beforeThreshold.Count; i++)
                        {
                            exceptionThrottler.Remove(beforeThreshold[i].Key);
                        }

                        // TODO: make threshold count configurable
                        if (exceptionThrottler.Count() >= 6)
                        {
                            exceptionThrottler.Clear();
                            this.Status = $"{ DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} - Too many exceptions, shutting down thread for now. Last exception: {ex.Message}";
                            Hubs.WorkerMonitor.Instance.NotifyObservers();
                            break; // break out of main while loop
                        }

                        this.Status = $"{ DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} - {ex.Message}";
                        Hubs.WorkerMonitor.Instance.NotifyObservers();

                        var sleepTimeMS = 2000 + (exceptionThrottler.Count() * 400);

                        // cap at 8 secs
                        sleepTimeMS = sleepTimeMS > 8000 ? 8000 : sleepTimeMS;

                        Thread.Sleep(sleepTimeMS);
                    }
                } // while IsRunning
            }
            catch (ThreadAbortException)
            {
                // ignore TAEs
            }
            catch (Exception ex)
            {
                this.log.Exception(ex);
            }
            finally
            {
                this.IsRunning = false;
                this.winThread = null;
                Hubs.WorkerMonitor.Instance.NotifyObservers();
            }
        } // Run
Esempio n. 11
0
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory, IHostApplicationLifetime applicationLifetime)
        {
            //app.UseDeveloperExceptionPage();

            // force instantiation on singletons

            {
                var pmInst = app.ApplicationServices.GetService <PluginLoader>();

                CommonNotificationThread.Instance = app.ApplicationServices.GetService <CommonNotificationThread>();

                BackgroundThreadPluginManager.Instance = app.ApplicationServices.GetService <BackgroundThreadPluginManager>();
                WorkerMonitor.Instance         = app.ApplicationServices.GetService <WorkerMonitor>();
                RealtimeMonitor.Instance       = app.ApplicationServices.GetService <RealtimeMonitor>();
                BackgroundTaskMonitor.Instance = app.ApplicationServices.GetService <BackgroundTaskMonitor>();

                ConnectionStringSecurity.Instance = app.ApplicationServices.GetService <ConnectionStringSecurity>();

                DotNetCoreCounterListener.Instance = app.ApplicationServices.GetService <DotNetCoreCounterListener>();
                DotNetCoreCounterListener.Instance.Start();


                {// More app startup stuff...but have a dependency on the singleton objects above. Can we move this somewhere else?
                    Log.Information("Initialising project object model");
                    // we can only initialise the Project structure once ConnectionStringSecurity exists
                    Settings.SettingsInstance.Instance.ProjectList.ForEach(p => p.AfterDeserializationInit());

                    Log.Information("Initialising plugin loader");
                    PluginLoader.Instance = pmInst;
                    PluginLoader.Instance.InitAsync().GetAwaiter().GetResult();

                    ServerMethodManager.RebuildCacheForAllApps();

                    Log.Information("Starting work spawner.");
                    WorkSpawner.Start();
                }
            }

            applicationLifetime.ApplicationStopped.Register(() =>
            {
                Log.Information("Application stopped");
            });

            applicationLifetime.ApplicationStopping.Register(() =>
            {
                Log.Information("Application is shutting down");
            });


            // app.Use(async (httpContext, next) =>
            // {
            //     //if (httpContext.Request.Path.Value.Contains("api/") && httpContext.Request.Method == "OPTIONS")
            //     if (httpContext.Request.Method.Equals("OPTIONS", StringComparison.OrdinalIgnoreCase))
            //     {
            //         httpContext.Response.Headers.Add("Access-Control-Max-Age", "600");
            //        // return;
            //     }
            //     await next();
            // });

            var webSocketOptions = new WebSocketOptions()
            {
                KeepAliveInterval = TimeSpan.FromSeconds(120),
                ReceiveBufferSize = 4 * 1024,
            };


            app.UseWebSockets(webSocketOptions);
            app.UseCors("CorsPolicy");


            // SPA (angular) route fallback
            app.Use(async(context, next) =>
            {
                await next();

                if (context.Response.StatusCode == 404 &&
                    !Path.HasExtension(context.Request.Path.Value) &&
                    !(context.Request.Path.Value?.ToLower().StartsWith("/api/") ?? false))
                {
                    context.Request.Path        = "/index.html";
                    context.Response.StatusCode = 200;

                    await next();
                }
            });


            /*****
             *          var mirrorSharpOptions = new MirrorSharpOptions()
             *          {
             *              SelfDebugEnabled = true,
             *              IncludeExceptionDetails = true,
             *              //SetOptionsFromClient = SetOptionsFromClientExtension()
             *              // CSharp = {
             *              //             MetadataReferences = ImmutableList.Create<MetadataReference>(all),
             *              //             CompilationOptions = compilationOptions
             *              //          },
             *              ExceptionLogger = new MirrorSharpExceptionLogger()
             *          }.SetupCSharp(cs =>
             *          {
             *              //cs.MetadataReferences = cs.MetadataReferences.Clear();
             *              //cs.AddMetadataReferencesFromFiles(all);
             *              cs.MetadataReferences = ImmutableList.Create<MetadataReference>(all);
             *              cs.CompilationOptions = compilationOptions;
             *
             *          });
             *
             *
             *          app.UseMirrorSharp(mirrorSharpOptions);
             */
            app.UseDefaultFiles();
            app.UseStaticFiles();

            // TODO: This outputs full request detail into log. Perhaps consider outputting this to a different detailed log
            //app.UseSerilogRequestLogging();
            app.UseSerilogRequestLogging(options =>
            {
                options.GetType().GetFields(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
                // Customize the message template
                //HTTP {RequestMethod} {RequestPath} responded {StatusCode} in {Elapsed:0.0000} ms
                options.MessageTemplate = "Req/Res: {ReqLen,7} {ResLen,7} {StatusCode} {Elapsed,7:0} ms {RequestMethod,4} {RequestPath}";

                //options.GetLevel = (httpContext, elapsed, ex) => Serilog.Events.LogEventLevel.Warning;


                // Attach additional properties to the request completion event
                options.EnrichDiagnosticContext = (diagnosticContext, httpContext) =>
                {
                    diagnosticContext.Set("ResLen", httpContext.Response.ContentLength ?? 0);
                    diagnosticContext.Set("ReqLen", httpContext.Request.ContentLength ?? 0);
                };
            });

            app.UseRouting();


            MetadataReference[] allMetadataReferences = null;

            try
            {
                allMetadataReferences = CSharpCompilerHelper.GetCommonMetadataReferences();

                var jsDALBasePluginPath = Path.GetFullPath("./plugins/jsdal-plugin.dll");

                if (File.Exists("./plugins/jsdal-plugin.dll"))
                {
                    Array.Resize(ref allMetadataReferences, allMetadataReferences.Length + 1);

                    allMetadataReferences[allMetadataReferences.Length - 1] = Microsoft.CodeAnalysis.MetadataReference.CreateFromFile(jsDALBasePluginPath);
                }
                else
                {
                    Log.Error($"Failed to find base plugin assembly at {jsDALBasePluginPath}");
                    SessionLog.Error($"Failed to find base plugin assembly at {jsDALBasePluginPath}");
                }
            }
            catch (Exception mex)
            {
                Log.Error(mex, "Failed to compile collection of metadata references");
            }

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapHub <Hubs.HomeDashboardHub>("/main-stats");
                endpoints.MapHub <Hubs.WorkerDashboardHub>("/worker-hub");
                endpoints.MapHub <Hubs.Performance.RealtimeHub>("/performance-realtime-hub");
                endpoints.MapHub <Hubs.HeartBeat.HeartBeatHub>("/heartbeat");
                endpoints.MapHub <Hubs.BackgroundTaskHub>("/bgtasks-hub");
                endpoints.MapHub <Hubs.BackgroundPluginHub>("/bgplugin-hub");
                endpoints.MapHub <Hubs.ExecHub>("/exec-hub");

                if (allMetadataReferences?.Length > 0)
                {
                    var compilationOptions = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary,
                                                                          //            generalDiagnosticOption: ReportDiagnostic.Suppress
                                                                          specificDiagnosticOptions: new Dictionary <string, ReportDiagnostic>
                    {
                        { "CS1701", ReportDiagnostic.Suppress },                                     // Binding redirects
                        { "CS1702", ReportDiagnostic.Suppress },
                        { "CS1705", ReportDiagnostic.Suppress }
                    }
                                                                          );

                    var mirrorSharpOptions = new MirrorSharpOptions()
                    {
                        SelfDebugEnabled        = true,
                        IncludeExceptionDetails = true
                                                  //SetOptionsFromClient = SetOptionsFromClientExtension()
                                                  // CSharp = {
                                                  //             MetadataReferences = ImmutableList.Create<MetadataReference>(all),
                                                  //             CompilationOptions = compilationOptions
                                                  //          },
                                                  //   ExceptionLogger = new MirrorSharpExceptionLogger()
                    }.SetupCSharp(cs =>
                    {
                        //cs.MetadataReferences = cs.MetadataReferences.Clear();
                        //cs.AddMetadataReferencesFromFiles(all);
                        cs.MetadataReferences = ImmutableList.Create <MetadataReference>(allMetadataReferences);
                        cs.CompilationOptions = compilationOptions;
                    });

                    endpoints.MapMirrorSharp("/mirrorsharp", mirrorSharpOptions);
                }
                else
                {
                    Log.Warning("Mirrorsharp not started because of errors builiding up metadata references");
                }
            });

            app.UseAuthentication();
            app.UseWebSockets();
            app.UseCookiePolicy();

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseHsts();

                // unhandled exceptions
                app.UseExceptionHandler(errorApp =>
                {
                    errorApp.Run(async context =>
                    {
                        try
                        {
                            var exceptionHandlerPathFeature = context.Features.Get <IExceptionHandlerPathFeature>();

                            var id = ExceptionLogger.LogException(exceptionHandlerPathFeature.Error, exceptionHandlerPathFeature.Path, "jsdal-server", null);

                            context.Response.StatusCode  = 500;
                            context.Response.ContentType = "text/plain";

                            await context.Response.WriteAsync($"Server error. Ref: {id}");
                            await context.Response.WriteAsync(new string(' ', 512)); // IE padding
                        }
                        catch (Exception ex)
                        {
                            Log.Error(ex, $"Failed to log unhandled exception because of:\r\n {ex.ToString()}");
                        }
                    });
                });
            }

            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });
        }
Esempio n. 12
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();
            }
        }
Esempio n. 13
0
        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")
        }
Esempio n. 14
0
        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);
            }
        }
Esempio n. 16
0
        //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);
        }
Esempio n. 17
0
        // 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);
            }
        }
Esempio n. 18
0
 private void OnUnloading(AssemblyLoadContext ctx)
 {
     SessionLog.Info($"Unloading assembly context {ctx.Name}");
     PluginLoader.RemoveAssemblyContextRef(this);
 }
Esempio n. 19
0
        // public void HandleAssemblyUpdated(PluginInfo pluginInfo)
        // {
        //     this.PluginInfo = pluginInfo;
        //     this.Process();
        // }

        public void CombineOutput(OM.Application appContext, ref Dictionary <string /*Namespace*/, List <Definition> > combinedJS, ref Dictionary <string /*Namespace*/, List <Definition> > combinedTSD, ref List <string> combinedConverterLookup)
        {
            // converters
            foreach (var c in this.ConverterLookup)
            {
                if (combinedConverterLookup.IndexOf(c) == -1)
                {
                    combinedConverterLookup.Add(c);
                }
            }

            foreach (var namespaceKV in this.JavaScriptDefinitions)
            {
                // js
                foreach (var definition in namespaceKV.Value)
                {
                    if (!combinedJS.ContainsKey(namespaceKV.Key))
                    {
                        combinedJS.Add(namespaceKV.Key, new List <Definition>());
                    }

                    if (combinedJS[namespaceKV.Key].FirstOrDefault(m => m.MethodName.Equals(definition.MethodName, StringComparison.Ordinal)) != null)
                    {
                        // TODO: Consider allowing overloads
                        SessionLog.Warning($"{appContext.Project.Name}/{appContext.Name} - ServerMethods - conflicting method name '{definition.MethodName}'.");
                        continue;
                    }

                    var hasConverter = definition.InputConverter != null || definition.OutputConverter != null || definition.ResultsConverter != null;

                    if (hasConverter)
                    {
                        var convertersSB = new StringBuilder("{ ");
                        var lst          = new List <string>();

                        string inputConverter  = definition.InputConverter;
                        string outputConverter = definition.OutputConverter;
                        string resultConverter = definition.ResultsConverter;

                        foreach (var converterJson in combinedConverterLookup)
                        {
                            if (inputConverter != null)
                            {
                                inputConverter = inputConverter.Replace(converterJson, combinedConverterLookup.IndexOf(converterJson).ToString());
                            }

                            if (outputConverter != null)
                            {
                                outputConverter = outputConverter.Replace(converterJson, combinedConverterLookup.IndexOf(converterJson).ToString());
                            }

                            if (resultConverter != null)
                            {
                                resultConverter = resultConverter.Replace(converterJson, combinedConverterLookup.IndexOf(converterJson).ToString());
                            }
                        }

                        if (inputConverter != null)
                        {
                            lst.Add($"input: {{ {inputConverter} }}");
                        }

                        if (outputConverter != null)
                        {
                            lst.Add($"output: {{ {outputConverter} }}");
                        }

                        if (resultConverter != null)
                        {
                            lst.Add($"results: {{ {resultConverter} }}");
                        }

                        convertersSB.Append(string.Join(", ", lst));

                        convertersSB.Append(" }");


                        definition.Line = definition.Line.Replace("<<CONV_SEP>>", ", ");
                        definition.Line = definition.Line.Replace("<<CONVERTERS>>", convertersSB.ToString());
                    }

                    combinedJS[namespaceKV.Key].Add(definition);
                }
            }

            // tsd
            foreach (var namespaceKV in this.TypescriptDefinitions)
            {
                foreach (var definition in namespaceKV.Value)
                {
                    if (!combinedTSD.ContainsKey(namespaceKV.Key))
                    {
                        combinedTSD.Add(namespaceKV.Key, new List <Definition>());
                    }

                    if (combinedTSD[namespaceKV.Key].FirstOrDefault(m => m.MethodName.Equals(definition.MethodName, StringComparison.Ordinal)) != null)
                    {
                        // just skip, should have already been handled on the .js side
                        continue;
                    }

                    combinedTSD[namespaceKV.Key].Add(definition);
                }
            }
        }
Esempio n. 20
0
        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);
            // }
        }