public void CreateEndpointInstances(IHubContext <Hubs.BackgroundPluginHub> hub) { IHubClients hubClients = hub.Clients; // TODO: Need to look at EP Creation Event and EP stopped & deleted event. var apps = Settings.SettingsInstance.Instance .ProjectList .SelectMany(proj => proj.Applications) .Where(app => app.IsPluginIncluded(this.PluginGuid.ToString())); var endpointCollection = apps.SelectMany(app => app.Endpoints); // create a default instance just to read the Default Value collection var defaultInstance = (BackgroundThreadPlugin)this.Assembly.CreateInstance(this.TypeInfo.FullName); var defaultConfig = defaultInstance.GetDefaultConfig(); if (defaultConfig?.ContainsKey("IsEnabled") ?? false) { var defIsEnabled = defaultConfig["IsEnabled"]; // TODO: Convert to better typed class (e.g. true/false) // TODO: Match up with Endpoint config. EP Config needs to be persisted somewhere } // TODO: For each BG Plugin catalog the 'server methods' available. (do this once per assembly, not per EP as they are the same for all EPs) foreach (var endpoint in endpointCollection) { try { // TODO: Look for an existing instance on the EP // TODO: If no longer ENABLED on EP kill instance? Won't currently be in collection above var existingInstance = FindPluginInstance(endpoint); if (existingInstance != null) { // no need to instantiate again continue; } var pluginInstance = (BackgroundThreadPlugin)this.Assembly.CreateInstance(this.TypeInfo.FullName); var initMethod = typeof(BackgroundThreadPlugin).GetMethod("Init", BindingFlags.Instance | BindingFlags.NonPublic); // var initMethod = typeof(BackgroundThreadPlugin).GetMethod("Init", BindingFlags.Instance | BindingFlags.NonPublic); if (initMethod != null) { var instanceWrapper = new BackgroundThreadPluginInstance(endpoint, pluginInstance); var logExceptionCallback = new Action <Exception, string>((exception, additionalInfo) => { // TODO: Throttle logging if it happens too frequently. Possibly stop plugin if too many exceptions? ExceptionLogger.LogException(exception, new Controllers.ExecController.ExecOptions() { project = endpoint.Application.Project.Name, application = endpoint.Application.Name, endpoint = endpoint.Name, schema = "BG PLUGIN", routine = this.PluginName, type = Controllers.ExecController.ExecType.BackgroundThread }, additionalInfo, $"BG PLUGIN - {this.PluginName}", endpoint.Pedigree); }); var openSqlConnectionCallback = new Func <SqlConnection>(() => { try { var execConn = endpoint.GetSqlConnection(); if (execConn == null) { throw new Exception($"Execution connection not configured on endpoint {endpoint.Pedigree}"); } var cb = new SqlConnectionStringBuilder(execConn.ConnectionStringDecrypted); cb.ApplicationName = $"{this.PluginName}"; var sqlCon = new SqlConnection(cb.ConnectionString); sqlCon.Open(); return(sqlCon); } catch (Exception ex) { ExceptionLogger.LogExceptionThrottled(ex, $"{this.PluginName}-{endpoint.Pedigree}::OpenSqlConnection", 20); throw; } }); var updateDataCallback = new Func <ExpandoObject, bool>(data => { dynamic eo = data; eo.InstanceId = instanceWrapper.Id; eo.Endpoint = endpoint.Pedigree; hubClients.Group(Hubs.BackgroundPluginHub.ADMIN_GROUP_NAME).SendAsync("updateData", (object)eo); return(true); }); var browserConsoleSendCallback = new Func <string, string, bool>((method, line) => { hubClients.Group(Hubs.BackgroundPluginHub.BROWSER_CONSOLE_GROUP_NAME).SendAsync(method, new { InstanceId = instanceWrapper.Id, Endpoint = endpoint.Pedigree, Line = line }); return(true); }); var addToGroupAsync = new Func <string, string, CancellationToken, Task>((connectionId, groupName, cancellationToken) => { return(hub.Groups.AddToGroupAsync(connectionId, $"{endpoint.Pedigree}.{groupName}", cancellationToken)); }); var sendToGroupsAsync = new Func <string, string, object[], Task>((groupName, methodName, args) => { groupName = $"{endpoint.Pedigree}.{groupName}"; if (args == null || args.Length == 0) { return(hub.Clients.Groups(groupName).SendAsync(methodName)); } else if (args.Length == 1) { return(hub.Clients.Groups(groupName).SendAsync(methodName, args[0])); } else if (args.Length == 2) { return(hub.Clients.Groups(groupName).SendAsync(methodName, args[0], args[1])); } else if (args.Length == 3) { return(hub.Clients.Groups(groupName).SendAsync(methodName, args[0], args[1], args[2])); } else if (args.Length == 4) { return(hub.Clients.Groups(groupName).SendAsync(methodName, args[0], args[1], args[2], args[3])); } else if (args.Length == 5) { return(hub.Clients.Groups(groupName).SendAsync(methodName, args[0], args[1], args[2], args[3], args[4])); } else if (args.Length == 6) { return(hub.Clients.Groups(groupName).SendAsync(methodName, args[0], args[1], args[2], args[3], args[4], args[5])); } else if (args.Length == 7) { return(hub.Clients.Groups(groupName).SendAsync(methodName, args[0], args[1], args[2], args[3], args[4], args[5], args[6])); } else if (args.Length == 8) { return(hub.Clients.Groups(groupName).SendAsync(methodName, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7])); } else if (args.Length == 9) { return(hub.Clients.Groups(groupName).SendAsync(methodName, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8])); } else if (args.Length == 10) { return(hub.Clients.Groups(groupName).SendAsync(methodName, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9])); } return(null); }); var dataCollectorBeginCallback = new Func <string, string, string>((schema, routine) => { string dataCollectorEntryShortId = DataCollectorThread.Enqueue(endpoint, new Controllers.ExecController.ExecOptions() { project = endpoint.Application.Project.Name, application = endpoint.Application.Name, endpoint = endpoint.Name, schema = schema, routine = routine, type = Controllers.ExecController.ExecType.BackgroundThread, inputParameters = new Dictionary <string, string>() // TODO: needs to be an input parameter }); return(dataCollectorEntryShortId); }); initMethod.Invoke(pluginInstance, new object[] { endpoint.Pedigree, logExceptionCallback, openSqlConnectionCallback, updateDataCallback, browserConsoleSendCallback, addToGroupAsync, sendToGroupsAsync, null /*configKeys*/, null /*configSource*/ }); { var setGetServicesFuncMethod = typeof(PluginBase).GetMethod("SetGetServicesFunc", BindingFlags.Instance | BindingFlags.NonPublic); if (setGetServicesFuncMethod != null) { setGetServicesFuncMethod.Invoke(pluginInstance, new object[] { new Func <Type, PluginService>(serviceType => { if (serviceType == typeof(BlobStoreBase)) { return(BlobStore.Instance); } return(null); }) }); } } this.AddEnpointInstance(endpoint, instanceWrapper); SessionLog.Info($"BG plugin '{this.PluginName}' instantiated successfully on endpoint {endpoint.Pedigree}"); } else { throw new Exception("Expected Init method not found"); } } catch (Exception ex) { SessionLog.Error($"Failed to instantiate plugin '{this.PluginName}' ({this.PluginGuid}) from assembly {this.Assembly.FullName} on endpoint {endpoint.Pedigree}. See exception that follows."); SessionLog.Exception(ex); } } }