Update: Plugin Updating
Updating plugins from the plugin catalogue, and automatic updating of plugins after a newer version of Disco is installed.
This commit is contained in:
@@ -101,6 +101,9 @@
|
||||
<Compile Include="Logging\Targets\LogLiveContext.cs" />
|
||||
<Compile Include="Logging\Targets\LogPersistContext.cs" />
|
||||
<Compile Include="Logging\Utilities.cs" />
|
||||
<Compile Include="Plugins\CommunityInterop\PluginLibraryUpdateTask.cs" />
|
||||
<Compile Include="Plugins\UpdatePluginsAfterDiscoUpdateTask.cs" />
|
||||
<Compile Include="Plugins\UpdatePluginTask.cs" />
|
||||
<Compile Include="Plugins\InstallPluginTask.cs" />
|
||||
<Compile Include="Plugins\PluginAttribute.cs" />
|
||||
<Compile Include="Plugins\PluginExtensions.cs" />
|
||||
@@ -126,7 +129,6 @@
|
||||
<Compile Include="Plugins\UnknownPluginException.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Tasks\ScheduledTask.cs" />
|
||||
<Compile Include="Tasks\ScheduledTaskCleanup.cs" />
|
||||
<Compile Include="Tasks\ScheduledTasks.cs" />
|
||||
<Compile Include="Tasks\ScheduledTasksLog.cs" />
|
||||
<Compile Include="Tasks\ScheduledTaskStatus.cs" />
|
||||
|
||||
@@ -0,0 +1,217 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Disco.Data.Repository;
|
||||
using Disco.Services.Plugins;
|
||||
using Disco.Services.Tasks;
|
||||
using Disco.Models.BI.Interop.Community;
|
||||
using System.Net;
|
||||
using System.Xml.Serialization;
|
||||
using System.IO;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
using Quartz;
|
||||
|
||||
namespace Disco.Services.Plugins.CommunityInterop
|
||||
{
|
||||
public class PluginLibraryUpdateTask : ScheduledTask
|
||||
{
|
||||
public override string TaskName { get { return "Disco Community - Update Plugin Library"; } }
|
||||
|
||||
public static string CurrentDiscoVersion()
|
||||
{
|
||||
var AssemblyVersion = typeof(PluginLibraryUpdateTask).Assembly.GetName().Version;
|
||||
return string.Format("{0}.{1}.{2:0000}.{3:0000}", AssemblyVersion.Major, AssemblyVersion.Minor, AssemblyVersion.Build, AssemblyVersion.Revision);
|
||||
}
|
||||
|
||||
protected override void ExecuteTask()
|
||||
{
|
||||
ExecuteTaskInternal(this.Status);
|
||||
Status.SetFinishedMessage("The Plugin Library Catalogue was updated.");
|
||||
}
|
||||
|
||||
internal static void ExecuteTaskInternal(ScheduledTaskStatus Status)
|
||||
{
|
||||
PluginLibraryUpdateRequest updateRequestBody;
|
||||
PluginLibraryUpdateResponse updateResult;
|
||||
string catalogueFile;
|
||||
PluginLibraryCompatibilityRequest compatRequestBody;
|
||||
PluginLibraryCompatibilityResponse compatResult;
|
||||
string compatibilityFile;
|
||||
|
||||
var DiscoBIVersion = CurrentDiscoVersion();
|
||||
HttpWebRequest webRequest;
|
||||
|
||||
#region Update
|
||||
|
||||
Status.UpdateStatus(1, "Updating Plugin Library Catalogue", "Building Request");
|
||||
|
||||
using (DiscoDataContext dbContext = new DiscoDataContext())
|
||||
{
|
||||
catalogueFile = Plugins.CatalogueFile(dbContext);
|
||||
|
||||
updateRequestBody = new PluginLibraryUpdateRequest()
|
||||
{
|
||||
DeploymentId = dbContext.DiscoConfiguration.DeploymentId,
|
||||
HostVersion = typeof(Plugins).Assembly.GetName().Version.ToString(4)
|
||||
};
|
||||
}
|
||||
|
||||
Status.UpdateStatus(10, "Sending Request");
|
||||
|
||||
webRequest = (HttpWebRequest)HttpWebRequest.Create(PluginLibraryUpdateUrl());
|
||||
webRequest.KeepAlive = false;
|
||||
|
||||
webRequest.ContentType = "application/json";
|
||||
webRequest.Method = WebRequestMethods.Http.Post;
|
||||
webRequest.UserAgent = string.Format("Disco/{0} (PluginLibrary)", DiscoBIVersion);
|
||||
|
||||
using (var wrStream = webRequest.GetRequestStream())
|
||||
{
|
||||
XmlSerializer xml = new XmlSerializer(typeof(PluginLibraryUpdateRequest));
|
||||
xml.Serialize(wrStream, updateRequestBody);
|
||||
}
|
||||
|
||||
Status.UpdateStatus(20, "Waiting for Response");
|
||||
|
||||
using (HttpWebResponse webResponse = (HttpWebResponse)webRequest.GetResponse())
|
||||
{
|
||||
if (webResponse.StatusCode == HttpStatusCode.OK)
|
||||
{
|
||||
Status.UpdateStatus(45, "Reading Response");
|
||||
using (var wResStream = webResponse.GetResponseStream())
|
||||
{
|
||||
XmlSerializer xml = new XmlSerializer(typeof(PluginLibraryUpdateResponse));
|
||||
updateResult = (PluginLibraryUpdateResponse)xml.Deserialize(wResStream);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Status.SetTaskException(new WebException(string.Format("Server responded with: [{0}] {1}", webResponse.StatusCode, webResponse.StatusDescription)));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!Directory.Exists(Path.GetDirectoryName(catalogueFile)))
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(catalogueFile));
|
||||
|
||||
using (FileStream fs = new FileStream(catalogueFile, FileMode.Create, FileAccess.Write, FileShare.None))
|
||||
{
|
||||
using (StreamWriter fsWriter = new StreamWriter(fs))
|
||||
{
|
||||
fsWriter.Write(JsonConvert.SerializeObject(updateResult));
|
||||
fsWriter.Flush();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Compatibility
|
||||
|
||||
Status.UpdateStatus(50, "Updating Plugin Library Compatibility", "Building Request");
|
||||
|
||||
using (DiscoDataContext dbContext = new DiscoDataContext())
|
||||
{
|
||||
compatibilityFile = Plugins.CompatibilityFile(dbContext);
|
||||
|
||||
compatRequestBody = new PluginLibraryCompatibilityRequest()
|
||||
{
|
||||
DeploymentId = dbContext.DiscoConfiguration.DeploymentId,
|
||||
HostVersion = typeof(Plugins).Assembly.GetName().Version.ToString(4)
|
||||
};
|
||||
}
|
||||
|
||||
Status.UpdateStatus(60, "Sending Request");
|
||||
|
||||
webRequest = (HttpWebRequest)HttpWebRequest.Create(PluginLibraryCompatibilityUrl());
|
||||
webRequest.KeepAlive = false;
|
||||
|
||||
webRequest.ContentType = "application/json";
|
||||
webRequest.Method = WebRequestMethods.Http.Post;
|
||||
webRequest.UserAgent = string.Format("Disco/{0} (PluginLibrary)", DiscoBIVersion);
|
||||
|
||||
using (var wrStream = webRequest.GetRequestStream())
|
||||
{
|
||||
XmlSerializer xml = new XmlSerializer(typeof(PluginLibraryCompatibilityRequest));
|
||||
xml.Serialize(wrStream, compatRequestBody);
|
||||
}
|
||||
|
||||
Status.UpdateStatus(70, "Waiting for Response");
|
||||
|
||||
using (HttpWebResponse webResponse = (HttpWebResponse)webRequest.GetResponse())
|
||||
{
|
||||
if (webResponse.StatusCode == HttpStatusCode.OK)
|
||||
{
|
||||
Status.UpdateStatus(95, "Reading Response");
|
||||
using (var wResStream = webResponse.GetResponseStream())
|
||||
{
|
||||
XmlSerializer xml = new XmlSerializer(typeof(PluginLibraryCompatibilityResponse));
|
||||
compatResult = (PluginLibraryCompatibilityResponse)xml.Deserialize(wResStream);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Status.SetTaskException(new WebException(string.Format("Server responded with: [{0}] {1}", webResponse.StatusCode, webResponse.StatusDescription)));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!Directory.Exists(Path.GetDirectoryName(compatibilityFile)))
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(compatibilityFile));
|
||||
|
||||
using (FileStream fs = new FileStream(compatibilityFile, FileMode.Create, FileAccess.Write, FileShare.None))
|
||||
{
|
||||
using (StreamWriter fsWriter = new StreamWriter(fs))
|
||||
{
|
||||
fsWriter.Write(JsonConvert.SerializeObject(compatResult));
|
||||
fsWriter.Flush();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
private static string PluginLibraryUpdateUrl()
|
||||
{
|
||||
return string.Concat(Disco.Data.Configuration.CommunityHelpers.CommunityUrl(), "DiscoPluginLibrary/V1");
|
||||
}
|
||||
private static string PluginLibraryCompatibilityUrl()
|
||||
{
|
||||
return string.Concat(Disco.Data.Configuration.CommunityHelpers.CommunityUrl(), "DiscoPluginLibrary/CompatibilityV1");
|
||||
}
|
||||
|
||||
public static ScheduledTaskStatus ScheduleNow()
|
||||
{
|
||||
|
||||
var taskStatus = ScheduledTasks.GetTaskStatuses(typeof(PluginLibraryUpdateTask)).Where(ts => ts.IsRunning).FirstOrDefault();
|
||||
if (taskStatus != null)
|
||||
return taskStatus;
|
||||
else
|
||||
{
|
||||
var t = new PluginLibraryUpdateTask();
|
||||
return t.ScheduleTask();
|
||||
}
|
||||
}
|
||||
public static ScheduledTaskStatus RunningStatus
|
||||
{
|
||||
get
|
||||
{
|
||||
return ScheduledTasks.GetTaskStatuses(typeof(PluginLibraryUpdateTask)).Where(ts => ts.IsRunning).FirstOrDefault();
|
||||
}
|
||||
}
|
||||
|
||||
public override void InitalizeScheduledTask(DiscoDataContext dbContext)
|
||||
{
|
||||
// Random time between midday and midnight.
|
||||
var rnd = new Random();
|
||||
|
||||
var rndHour = rnd.Next(12, 23);
|
||||
var rndMinute = rnd.Next(0, 59);
|
||||
|
||||
TriggerBuilder triggerBuilder = TriggerBuilder.Create().
|
||||
WithSchedule(CronScheduleBuilder.DailyAtHourAndMinute(rndHour, rndMinute));
|
||||
|
||||
this.ScheduleTask(triggerBuilder);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -158,6 +158,10 @@ namespace Disco.Services.Plugins
|
||||
{
|
||||
if (ScheduledTasks.GetTaskStatuses(typeof(InstallPluginTask)).Where(s => s.IsRunning).Count() > 0)
|
||||
throw new InvalidOperationException("A plugin is already being Installed");
|
||||
if (ScheduledTasks.GetTaskStatuses(typeof(UpdatePluginTask)).Where(s => s.IsRunning).Count() > 0)
|
||||
throw new InvalidOperationException("A plugin is being Updated");
|
||||
if (ScheduledTasks.GetTaskStatuses(typeof(UninstallPluginTask)).Where(s => s.IsRunning).Count() > 0)
|
||||
throw new InvalidOperationException("A plugin is being Uninstalled");
|
||||
|
||||
JobDataMap taskData = new JobDataMap() { { "PackageUrl", PackageUrl }, { "PackageFilePath", PackageFilePath }, { "DeletePackageAfterInstall", DeletePackageAfterInstall } };
|
||||
|
||||
|
||||
@@ -348,6 +348,14 @@ namespace Disco.Services.Plugins
|
||||
return handler;
|
||||
}
|
||||
[JsonIgnore]
|
||||
public string ConfigurationUrl
|
||||
{
|
||||
get
|
||||
{
|
||||
return string.Format("/Config/Plugins/{0}", HttpUtility.UrlEncode(this.Id));
|
||||
}
|
||||
}
|
||||
[JsonIgnore]
|
||||
public bool HasWebHandler
|
||||
{
|
||||
get
|
||||
@@ -371,6 +379,14 @@ namespace Disco.Services.Plugins
|
||||
|
||||
return handler;
|
||||
}
|
||||
[JsonIgnore]
|
||||
public string WebHandlerUrl
|
||||
{
|
||||
get
|
||||
{
|
||||
return string.Format("/Plugin/{0}", HttpUtility.UrlEncode(this.Id));
|
||||
}
|
||||
}
|
||||
|
||||
public Tuple<string, string> WebResourcePath(string Resource)
|
||||
{
|
||||
|
||||
@@ -10,6 +10,7 @@ using System.IO.Compression;
|
||||
using Disco.Models.BI.Interop.Community;
|
||||
using System.Web;
|
||||
using Newtonsoft.Json;
|
||||
using System.Threading;
|
||||
|
||||
namespace Disco.Services.Plugins
|
||||
{
|
||||
@@ -477,6 +478,27 @@ namespace Disco.Services.Plugins
|
||||
return categoryDisplayNames;
|
||||
}
|
||||
|
||||
#region Restart App
|
||||
private static object _restartTimerLock = new object();
|
||||
private static Timer _restartTimer;
|
||||
internal static void RestartApp(int DelayMilliseconds)
|
||||
{
|
||||
lock (_restartTimerLock)
|
||||
{
|
||||
if (_restartTimer != null)
|
||||
{
|
||||
_restartTimer.Dispose();
|
||||
}
|
||||
|
||||
_restartTimer = new Timer((state) =>
|
||||
{
|
||||
HttpRuntime.UnloadAppDomain();
|
||||
//AppDomain.Unload(AppDomain.CurrentDomain);
|
||||
}, null, DelayMilliseconds, Timeout.Infinite);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Plugin Referenced Assemblies Resolving
|
||||
|
||||
public static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
|
||||
|
||||
@@ -45,13 +45,17 @@ namespace Disco.Services.Plugins
|
||||
}
|
||||
|
||||
this.Status.Finished("Restarting Disco, please wait...", "/Config/Plugins");
|
||||
RestartApp(1500);
|
||||
Plugins.RestartApp(1500);
|
||||
}
|
||||
|
||||
public static ScheduledTaskStatus UninstallPlugin(PluginManifest Manifest, bool UninstallData)
|
||||
{
|
||||
if (ScheduledTasks.GetTaskStatuses(typeof(InstallPluginTask)).Where(s => s.IsRunning).Count() > 0)
|
||||
throw new InvalidOperationException("A plugin is already being Uninstalled");
|
||||
if (ScheduledTasks.GetTaskStatuses(typeof(UpdatePluginTask)).Where(s => s.IsRunning).Count() > 0)
|
||||
throw new InvalidOperationException("A plugin is being Updated");
|
||||
if (ScheduledTasks.GetTaskStatuses(typeof(InstallPluginTask)).Where(s => s.IsRunning).Count() > 0)
|
||||
throw new InvalidOperationException("A plugin is being Installed");
|
||||
|
||||
JobDataMap taskData = new JobDataMap() { { "PluginManifest", Manifest }, { "UninstallData", UninstallData } };
|
||||
|
||||
@@ -59,26 +63,5 @@ namespace Disco.Services.Plugins
|
||||
|
||||
return instance.ScheduleTask(taskData);
|
||||
}
|
||||
|
||||
#region Restart App
|
||||
private static object _restartTimerLock = new object();
|
||||
private static Timer _restartTimer;
|
||||
private void RestartApp(int DelayMilliseconds)
|
||||
{
|
||||
lock (_restartTimerLock)
|
||||
{
|
||||
if (_restartTimer != null)
|
||||
{
|
||||
_restartTimer.Dispose();
|
||||
}
|
||||
|
||||
_restartTimer = new Timer((state) =>
|
||||
{
|
||||
HttpRuntime.UnloadAppDomain();
|
||||
//AppDomain.Unload(AppDomain.CurrentDomain);
|
||||
}, null, DelayMilliseconds, Timeout.Infinite);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,252 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using Disco.Data.Repository;
|
||||
using Disco.Models.BI.Interop.Community;
|
||||
using Disco.Services.Tasks;
|
||||
using Quartz;
|
||||
|
||||
namespace Disco.Services.Plugins
|
||||
{
|
||||
public class UpdatePluginTask : ScheduledTask
|
||||
{
|
||||
public override string TaskName { get { return "Updating Plugin/s"; } }
|
||||
|
||||
protected override void ExecuteTask()
|
||||
{
|
||||
string pluginId = (string)this.ExecutionContext.JobDetail.JobDataMap["PluginId"];
|
||||
string packageFilePath = (string)this.ExecutionContext.JobDetail.JobDataMap["PackageFilePath"];
|
||||
|
||||
PluginLibraryUpdateResponse catalogue;
|
||||
string pluginPackagesLocation;
|
||||
|
||||
if (!Plugins.PluginsLoaded)
|
||||
throw new InvalidOperationException("Plugins have not been initialized");
|
||||
|
||||
List<Tuple<PluginManifest, string, PluginLibraryItem>> updatePlugins;
|
||||
|
||||
using (DiscoDataContext dbContext = new DiscoDataContext())
|
||||
{
|
||||
catalogue = Plugins.LoadCatalogue(dbContext);
|
||||
pluginPackagesLocation = dbContext.DiscoConfiguration.PluginPackagesLocation;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(pluginId))
|
||||
{
|
||||
if (string.IsNullOrEmpty(packageFilePath))
|
||||
{
|
||||
// Update Single from Catalogue
|
||||
PluginManifest existingManifest = Plugins.GetPlugin(pluginId);
|
||||
var catalogueItem = catalogue.Plugins.FirstOrDefault(p => p.Id == existingManifest.Id);
|
||||
|
||||
if (catalogueItem == null)
|
||||
throw new InvalidOperationException("No updates are available for this Plugin");
|
||||
if (Version.Parse(catalogueItem.LatestVersion) <= existingManifest.Version)
|
||||
throw new InvalidOperationException("Only newer versions can be used to update a plugin");
|
||||
|
||||
updatePlugins = new List<Tuple<PluginManifest, string, PluginLibraryItem>>() {
|
||||
new Tuple<PluginManifest,string,PluginLibraryItem>(existingManifest, null, catalogueItem)
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
// Update Single from Local
|
||||
PluginManifest existingManifest = Plugins.GetPlugin(pluginId);
|
||||
updatePlugins = new List<Tuple<PluginManifest, string, PluginLibraryItem>>() {
|
||||
new Tuple<PluginManifest,string,PluginLibraryItem>(existingManifest, packageFilePath, null)
|
||||
};
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Update All
|
||||
updatePlugins = Plugins.GetPlugins().Join((IEnumerable<PluginLibraryItem>)catalogue.Plugins, manifest => manifest.Id, update => update.Id, (manifest, update) => new Tuple<PluginManifest, string, PluginLibraryItem>(manifest, null, update)).Where(i => Version.Parse(i.Item3.LatestVersion) > i.Item1.Version).ToList();
|
||||
}
|
||||
|
||||
if (updatePlugins == null || updatePlugins.Count == 0)
|
||||
{
|
||||
this.Status.Finished("No plugins to update...", "/Config/Plugins");
|
||||
return;
|
||||
}
|
||||
|
||||
ExecuteTaskInternal(this.Status, pluginPackagesLocation, updatePlugins);
|
||||
|
||||
this.Status.Finished("Restarting Disco, please wait...", "/Config/Plugins");
|
||||
Plugins.RestartApp(1500);
|
||||
}
|
||||
|
||||
internal static void UpdateOffline(ScheduledTaskStatus Status)
|
||||
{
|
||||
string pluginsLocation;
|
||||
string pluginPackagesLocation;
|
||||
PluginLibraryUpdateResponse pluginCatalogue;
|
||||
List<Tuple<PluginManifest, string, PluginLibraryItem>> UpdatePlugins = new List<Tuple<PluginManifest, string, PluginLibraryItem>>();
|
||||
|
||||
using (DiscoDataContext dbContext = new DiscoDataContext())
|
||||
{
|
||||
pluginCatalogue = Plugins.LoadCatalogue(dbContext);
|
||||
pluginsLocation = dbContext.DiscoConfiguration.PluginsLocation;
|
||||
pluginPackagesLocation = dbContext.DiscoConfiguration.PluginPackagesLocation;
|
||||
}
|
||||
|
||||
DirectoryInfo pluginDirectoryRoot = new DirectoryInfo(pluginsLocation);
|
||||
if (pluginDirectoryRoot.Exists)
|
||||
{
|
||||
foreach (DirectoryInfo pluginDirectory in pluginDirectoryRoot.EnumerateDirectories())
|
||||
{
|
||||
string pluginManifestFilename = Path.Combine(pluginDirectory.FullName, "manifest.json");
|
||||
if (File.Exists(pluginManifestFilename))
|
||||
{
|
||||
PluginManifest pluginManifest = null;
|
||||
try
|
||||
{
|
||||
pluginManifest = PluginManifest.FromPluginManifestFile(pluginManifestFilename);
|
||||
}
|
||||
catch (Exception) { }
|
||||
|
||||
if (pluginManifest != null)
|
||||
{
|
||||
// Check for Update
|
||||
var catalogueItem = pluginCatalogue.Plugins.FirstOrDefault(i => i.Id == pluginManifest.Id && Version.Parse(i.LatestVersion) > pluginManifest.Version);
|
||||
|
||||
if (catalogueItem != null)
|
||||
{ // Update Available
|
||||
UpdatePlugins.Add(new Tuple<PluginManifest, string, PluginLibraryItem>(pluginManifest, null, catalogueItem));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (UpdatePlugins.Count > 0)
|
||||
{
|
||||
ExecuteTaskInternal(Status, pluginPackagesLocation, UpdatePlugins);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void ExecuteTaskInternal(ScheduledTaskStatus Status, string pluginPackagesLocation, List<Tuple<PluginManifest, string, PluginLibraryItem>> UpdatePlugins)
|
||||
{
|
||||
while (UpdatePlugins.Count > 0)
|
||||
{
|
||||
var updatePlugin = UpdatePlugins[0];
|
||||
var existingManifest = updatePlugin.Item1;
|
||||
var packageTempFilePath = updatePlugin.Item2;
|
||||
var catalogueItem = updatePlugin.Item3;
|
||||
UpdatePlugins.Remove(updatePlugin);
|
||||
|
||||
var pluginId = existingManifest != null ? existingManifest.Id : catalogueItem.Id;
|
||||
var pluginName = existingManifest != null ? existingManifest.Name : catalogueItem.Name;
|
||||
|
||||
if (string.IsNullOrEmpty(packageTempFilePath))
|
||||
{
|
||||
// Download Update
|
||||
|
||||
Status.UpdateStatus(0, string.Format("Downloading Plugin Package: {0}", pluginName), "Connecting...");
|
||||
packageTempFilePath = Path.Combine(pluginPackagesLocation, string.Format("{0}.discoPlugin", pluginId));
|
||||
|
||||
if (File.Exists(packageTempFilePath))
|
||||
File.Delete(packageTempFilePath);
|
||||
|
||||
if (!Directory.Exists(Path.GetDirectoryName(packageTempFilePath)))
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(packageTempFilePath));
|
||||
|
||||
// Need to Download the Package
|
||||
WebClient downloader = new WebClient();
|
||||
DateTime progressExpires = DateTime.Now;
|
||||
downloader.DownloadProgressChanged += (sender, e) =>
|
||||
{
|
||||
Console.WriteLine(e.ProgressPercentage);
|
||||
if (progressExpires <= DateTime.Now)
|
||||
{
|
||||
Status.UpdateStatus(e.ProgressPercentage, string.Format("{0} of {1} KB downloaded", e.BytesReceived / 1024, e.TotalBytesToReceive / 1024));
|
||||
// Throttle Updates for SignalR
|
||||
progressExpires = DateTime.Now.AddMilliseconds(250);
|
||||
}
|
||||
};
|
||||
downloader.DownloadFileTaskAsync(new Uri(catalogueItem.LatestDownloadUrl), packageTempFilePath).Wait();
|
||||
}
|
||||
|
||||
Status.UpdateStatus(10, "Opening Plugin Package", Path.GetFileName(packageTempFilePath));
|
||||
|
||||
PluginManifest updateManifest;
|
||||
|
||||
using (var packageStream = File.OpenRead(packageTempFilePath))
|
||||
{
|
||||
using (ZipArchive packageArchive = new ZipArchive(packageStream, ZipArchiveMode.Read, false))
|
||||
{
|
||||
|
||||
ZipArchiveEntry packageManifestEntry = packageArchive.GetEntry("manifest.json");
|
||||
if (packageManifestEntry == null)
|
||||
throw new InvalidDataException("The plugin package does not contain the 'manifest.json' entry");
|
||||
|
||||
using (Stream packageManifestStream = packageManifestEntry.Open())
|
||||
{
|
||||
updateManifest = PluginManifest.FromPluginManifestFile(packageManifestStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure not already installed
|
||||
if (existingManifest != null)
|
||||
if (updateManifest.Version <= existingManifest.Version)
|
||||
throw new InvalidOperationException("Only newer versions can be used to update a plugin");
|
||||
|
||||
Status.UpdateStatus(20, string.Format("{0} [{1} v{2}] by {3}", updateManifest.Name, updateManifest.Id, updateManifest.Version.ToString(4), updateManifest.Author), "Initializing Update Environment");
|
||||
|
||||
using (DiscoDataContext dbContext = new DiscoDataContext())
|
||||
{
|
||||
// Check for Compatibility
|
||||
var compatibilityData = Plugins.LoadCompatibilityData(dbContext);
|
||||
var pluginCompatibility = compatibilityData.Plugins.FirstOrDefault(i => i.Id.Equals(updateManifest.Id, StringComparison.InvariantCultureIgnoreCase) && updateManifest.Version == Version.Parse(i.Version));
|
||||
if (pluginCompatibility != null && !pluginCompatibility.Compatible)
|
||||
throw new InvalidOperationException(string.Format("The plugin [{0} v{1}] is not compatible: {2}", updateManifest.Id, updateManifest.VersionFormatted, pluginCompatibility.Reason));
|
||||
|
||||
var updatePluginPath = Path.Combine(dbContext.DiscoConfiguration.PluginsLocation, string.Format("{0}.discoPlugin", updateManifest.Id));
|
||||
File.Move(packageTempFilePath, updatePluginPath);
|
||||
|
||||
if (existingManifest != null && Plugins.PluginsLoaded)
|
||||
{
|
||||
PluginsLog.LogBeforeUpdate(existingManifest, updateManifest);
|
||||
existingManifest.BeforePluginUpdate(dbContext, updateManifest, Status);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static ScheduledTaskStatus UpdateHelper(string PluginId = null, string PackageFilePath = null)
|
||||
{
|
||||
if (ScheduledTasks.GetTaskStatuses(typeof(UpdatePluginTask)).Where(s => s.IsRunning).Count() > 0)
|
||||
throw new InvalidOperationException("A plugin is already being Updated");
|
||||
if (ScheduledTasks.GetTaskStatuses(typeof(UninstallPluginTask)).Where(s => s.IsRunning).Count() > 0)
|
||||
throw new InvalidOperationException("A plugin is being Uninstalled");
|
||||
if (ScheduledTasks.GetTaskStatuses(typeof(InstallPluginTask)).Where(s => s.IsRunning).Count() > 0)
|
||||
throw new InvalidOperationException("A plugin is being Installed");
|
||||
|
||||
JobDataMap taskData = new JobDataMap() { { "PluginId", PluginId }, { "PackageFilePath", PackageFilePath } };
|
||||
|
||||
var instance = new UpdatePluginTask();
|
||||
|
||||
return instance.ScheduleTask(taskData);
|
||||
}
|
||||
|
||||
public static ScheduledTaskStatus UpdateLocalPlugin(string PluginId, string PackageFilePath)
|
||||
{
|
||||
return UpdateHelper(PluginId, PackageFilePath);
|
||||
}
|
||||
public static ScheduledTaskStatus UpdatePlugin(string PluginId)
|
||||
{
|
||||
return UpdateHelper(PluginId);
|
||||
}
|
||||
public static ScheduledTaskStatus UpdateAllPlugins()
|
||||
{
|
||||
return UpdateHelper();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Disco.Services.Tasks;
|
||||
|
||||
namespace Disco.Services.Plugins
|
||||
{
|
||||
public class UpdatePluginsAfterDiscoUpdateTask : ScheduledTask
|
||||
{
|
||||
private static object _startLock = new object();
|
||||
|
||||
public override string TaskName { get { return "Updating Disco Plugins"; } }
|
||||
|
||||
protected override void ExecuteTask()
|
||||
{
|
||||
this.Status.UpdateStatus(0, "Updating plugins after Disco update", "Starting, please wait...");
|
||||
|
||||
// Wait for App to Load (6 Seconds)
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
this.Status.UpdateStatus(10 * i);
|
||||
System.Threading.Thread.Sleep(600);
|
||||
}
|
||||
|
||||
// Update Catalogue
|
||||
CommunityInterop.PluginLibraryUpdateTask.ExecuteTaskInternal(this.Status);
|
||||
|
||||
// Update all Plugins
|
||||
UpdatePluginTask.UpdateOffline(this.Status);
|
||||
|
||||
// Restart
|
||||
this.Status.Finished("Restarting Disco, please wait...", "/");
|
||||
Plugins.RestartApp(1500);
|
||||
}
|
||||
|
||||
public static ScheduledTaskStatus UpdateDiscoPlugins(bool ReturnExistingStatusIfRunning)
|
||||
{
|
||||
if (ScheduledTasks.GetTaskStatuses(typeof(UpdatePluginTask)).Where(s => s.IsRunning).Count() > 0)
|
||||
throw new InvalidOperationException("A plugin is already being Updated");
|
||||
if (ScheduledTasks.GetTaskStatuses(typeof(UninstallPluginTask)).Where(s => s.IsRunning).Count() > 0)
|
||||
throw new InvalidOperationException("A plugin is being Uninstalled");
|
||||
if (ScheduledTasks.GetTaskStatuses(typeof(InstallPluginTask)).Where(s => s.IsRunning).Count() > 0)
|
||||
throw new InvalidOperationException("A plugin is being Installed");
|
||||
|
||||
var existingTask = ScheduledTasks.GetTaskStatuses(typeof(UpdatePluginsAfterDiscoUpdateTask)).Where(s => s.IsRunning).FirstOrDefault();
|
||||
if (existingTask != null)
|
||||
{
|
||||
if (ReturnExistingStatusIfRunning)
|
||||
return existingTask;
|
||||
else
|
||||
throw new InvalidOperationException("Plugins are already being updated");
|
||||
}
|
||||
|
||||
lock (_startLock)
|
||||
{
|
||||
existingTask = ScheduledTasks.GetTaskStatuses(typeof(UpdatePluginsAfterDiscoUpdateTask)).Where(s => s.IsRunning).FirstOrDefault();
|
||||
if (existingTask != null)
|
||||
{
|
||||
if (ReturnExistingStatusIfRunning)
|
||||
return existingTask;
|
||||
else
|
||||
throw new InvalidOperationException("Plugins are already being updated");
|
||||
}
|
||||
|
||||
var instance = new UpdatePluginsAfterDiscoUpdateTask();
|
||||
var status = instance.ScheduleTask();
|
||||
|
||||
// Give the scheduler a chance to start the task
|
||||
System.Threading.Thread.Sleep(150);
|
||||
|
||||
return status;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.2.0212.1702")]
|
||||
[assembly: AssemblyFileVersion("1.2.0212.1702")]
|
||||
[assembly: AssemblyVersion("1.2.0214.1848")]
|
||||
[assembly: AssemblyFileVersion("1.2.0214.1848")]
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Quartz;
|
||||
|
||||
namespace Disco.Services.Tasks
|
||||
{
|
||||
|
||||
}
|
||||
@@ -1,190 +1,193 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Quartz;
|
||||
using Quartz.Impl;
|
||||
using Disco.Data.Repository;
|
||||
|
||||
namespace Disco.Services.Tasks
|
||||
{
|
||||
public static class ScheduledTasks
|
||||
{
|
||||
internal const string SchedulerGroupName = "DiscoScheduledTasks";
|
||||
private static IScheduler _TaskScheduler;
|
||||
|
||||
private static object _RunningTasksLock = new object();
|
||||
private static List<ScheduledTaskStatus> _RunningTasks = new List<ScheduledTaskStatus>();
|
||||
|
||||
public static void InitalizeScheduledTasks(DiscoDataContext dbContext, ISchedulerFactory SchedulerFactory)
|
||||
{
|
||||
ScheduledTasksLog.LogInitializingScheduledTasks();
|
||||
|
||||
try
|
||||
{
|
||||
_TaskScheduler = SchedulerFactory.GetScheduler();
|
||||
_TaskScheduler.Start();
|
||||
|
||||
// Scheduled Cleanup
|
||||
ScheduledTaskCleanup.Schedule(_TaskScheduler);
|
||||
|
||||
// Discover DiscoScheduledTask
|
||||
var appDomain = AppDomain.CurrentDomain;
|
||||
|
||||
var scheduledTaskTypes = (from a in appDomain.GetAssemblies()
|
||||
where !a.GlobalAssemblyCache && !a.IsDynamic
|
||||
from type in a.GetTypes()
|
||||
where typeof(ScheduledTask).IsAssignableFrom(type) && !type.IsAbstract
|
||||
select type);
|
||||
foreach (Type scheduledTaskType in scheduledTaskTypes)
|
||||
{
|
||||
ScheduledTask instance = (ScheduledTask)Activator.CreateInstance(scheduledTaskType);
|
||||
try
|
||||
{
|
||||
instance.InitalizeScheduledTask(dbContext);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ScheduledTasksLog.LogInitializeException(ex, scheduledTaskType);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ScheduledTasksLog.LogInitializeException(ex);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static ScheduledTaskStatus GetTaskStatus(string TaskSessionId)
|
||||
{
|
||||
return _RunningTasks.Where(t => t.SessionId == TaskSessionId).FirstOrDefault();
|
||||
}
|
||||
public static List<ScheduledTaskStatus> GetTaskStatuses(Type TaskType)
|
||||
{
|
||||
return _RunningTasks.Where(t => t.TaskType == TaskType).ToList();
|
||||
}
|
||||
public static List<ScheduledTaskStatus> GetTaskStatuses()
|
||||
{
|
||||
return _RunningTasks.ToList();
|
||||
}
|
||||
|
||||
public static ScheduledTaskStatus RegisterTask(ScheduledTask Task)
|
||||
{
|
||||
return RegisterTask(Task, null);
|
||||
}
|
||||
public static ScheduledTaskStatus RegisterTask(ScheduledTask Task, TriggerBuilder TaskBuilder)
|
||||
{
|
||||
var sessionId = Guid.NewGuid().ToString("D");
|
||||
var triggerKey = GenerateTriggerKey();
|
||||
var taskType = Task.GetType();
|
||||
|
||||
var taskStatus = new ScheduledTaskStatus(Task, sessionId, triggerKey.Name);
|
||||
|
||||
lock (_RunningTasksLock)
|
||||
{
|
||||
if (Task.SingleInstanceTask)
|
||||
{
|
||||
var existingGuid = _RunningTasks.Where(t => t.IsRunning && t.TaskType == taskType).Select(t => t.SessionId).FirstOrDefault();
|
||||
if (existingGuid != null)
|
||||
throw new InvalidOperationException(string.Format("This Single-Instance Task is already running, SessionId: {0}", existingGuid));
|
||||
}
|
||||
_RunningTasks.Add(taskStatus);
|
||||
}
|
||||
|
||||
if (TaskBuilder != null)
|
||||
{
|
||||
ITrigger trigger = TaskBuilder.WithIdentity(triggerKey).Build();
|
||||
IJobDetail jobDetails = new JobDetailImpl(sessionId, taskType)
|
||||
{
|
||||
Description = Task.TaskName,
|
||||
JobDataMap = trigger.JobDataMap
|
||||
};
|
||||
|
||||
_TaskScheduler.ScheduleJob(jobDetails, trigger);
|
||||
|
||||
var nextTriggerTime = trigger.GetNextFireTimeUtc();
|
||||
if (nextTriggerTime.HasValue)
|
||||
taskStatus.SetNextScheduledTimestamp(nextTriggerTime.Value.LocalDateTime);
|
||||
}
|
||||
|
||||
return taskStatus;
|
||||
}
|
||||
public static bool UnregisterTask(this ScheduledTask Task)
|
||||
{
|
||||
lock (_RunningTasksLock)
|
||||
{
|
||||
if (_RunningTasks.Contains(Task.Status))
|
||||
{
|
||||
//_RunningTasks.Remove(Task.Status);
|
||||
_TaskScheduler.UnscheduleJob(Task.ExecutionContext.Trigger.Key);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public static bool UnregisterTask(this ScheduledTaskStatus TaskStatus)
|
||||
{
|
||||
lock (_RunningTasksLock)
|
||||
{
|
||||
if (_RunningTasks.Contains(TaskStatus))
|
||||
{
|
||||
//_RunningTasks.Remove(Task.Status);
|
||||
_TaskScheduler.UnscheduleJob(new TriggerKey(TaskStatus.TriggerKey, SchedulerGroupName));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static TriggerKey GenerateTriggerKey()
|
||||
{
|
||||
return new TriggerKey(Guid.NewGuid().ToString("D"), SchedulerGroupName);
|
||||
}
|
||||
|
||||
public static ScheduledTaskStatus GetDiscoScheduledTaskStatus(this IJobExecutionContext context)
|
||||
{
|
||||
return GetTaskStatus(context.JobDetail.Key.Name);
|
||||
}
|
||||
|
||||
private class ScheduledTaskCleanup : IJob
|
||||
{
|
||||
public void Execute(IJobExecutionContext context)
|
||||
{
|
||||
lock (ScheduledTasks._RunningTasksLock)
|
||||
{
|
||||
// Lifetime = 5mins
|
||||
var expiredTime = DateTime.Now.AddMinutes(-1);
|
||||
var expiredTasks = ScheduledTasks._RunningTasks.Where(
|
||||
t => !t.IsRunning &&
|
||||
!t.NextScheduledTimestamp.HasValue &&
|
||||
t.FinishedTimestamp < expiredTime
|
||||
).ToArray();
|
||||
|
||||
foreach (var expiredTask in expiredTasks)
|
||||
ScheduledTasks._RunningTasks.Remove(expiredTask);
|
||||
}
|
||||
}
|
||||
public static void Schedule(IScheduler TaskScheduler)
|
||||
{
|
||||
// Next 10min interval
|
||||
DateTime now = DateTime.Now;
|
||||
int mins = (10 - (now.Minute % 10));
|
||||
if (mins < 2)
|
||||
mins += 10;
|
||||
DateTimeOffset startAt = new DateTimeOffset(now).AddMinutes(mins).AddSeconds(now.Second * -1).AddMilliseconds(now.Millisecond * -1);
|
||||
ITrigger trigger = TriggerBuilder.Create()
|
||||
.StartAt(startAt)
|
||||
.WithSchedule(SimpleScheduleBuilder.RepeatMinutelyForever(10))
|
||||
.WithIdentity("ScheduledTaskCleanupTrigger", ScheduledTasks.SchedulerGroupName + "_System")
|
||||
.Build();
|
||||
|
||||
IJobDetail job = JobBuilder.Create<ScheduledTaskCleanup>()
|
||||
.WithIdentity("ScheduledTaskCleanupJob", ScheduledTasks.SchedulerGroupName + "_System")
|
||||
.Build();
|
||||
|
||||
_TaskScheduler.ScheduleJob(job, trigger);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Quartz;
|
||||
using Quartz.Impl;
|
||||
using Disco.Data.Repository;
|
||||
|
||||
namespace Disco.Services.Tasks
|
||||
{
|
||||
public static class ScheduledTasks
|
||||
{
|
||||
internal const string SchedulerGroupName = "DiscoScheduledTasks";
|
||||
private static IScheduler _TaskScheduler;
|
||||
|
||||
private static object _RunningTasksLock = new object();
|
||||
private static List<ScheduledTaskStatus> _RunningTasks = new List<ScheduledTaskStatus>();
|
||||
|
||||
public static void InitalizeScheduledTasks(DiscoDataContext dbContext, ISchedulerFactory SchedulerFactory, bool InitiallySchedule)
|
||||
{
|
||||
ScheduledTasksLog.LogInitializingScheduledTasks();
|
||||
|
||||
try
|
||||
{
|
||||
_TaskScheduler = SchedulerFactory.GetScheduler();
|
||||
_TaskScheduler.Start();
|
||||
|
||||
// Scheduled Cleanup
|
||||
ScheduledTaskCleanup.Schedule(_TaskScheduler);
|
||||
|
||||
if (InitiallySchedule)
|
||||
{
|
||||
// Discover DiscoScheduledTask
|
||||
var appDomain = AppDomain.CurrentDomain;
|
||||
|
||||
var scheduledTaskTypes = (from a in appDomain.GetAssemblies()
|
||||
where !a.GlobalAssemblyCache && !a.IsDynamic
|
||||
from type in a.GetTypes()
|
||||
where typeof(ScheduledTask).IsAssignableFrom(type) && !type.IsAbstract
|
||||
select type);
|
||||
foreach (Type scheduledTaskType in scheduledTaskTypes)
|
||||
{
|
||||
ScheduledTask instance = (ScheduledTask)Activator.CreateInstance(scheduledTaskType);
|
||||
try
|
||||
{
|
||||
instance.InitalizeScheduledTask(dbContext);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ScheduledTasksLog.LogInitializeException(ex, scheduledTaskType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ScheduledTasksLog.LogInitializeException(ex);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static ScheduledTaskStatus GetTaskStatus(string TaskSessionId)
|
||||
{
|
||||
return _RunningTasks.Where(t => t.SessionId == TaskSessionId).FirstOrDefault();
|
||||
}
|
||||
public static List<ScheduledTaskStatus> GetTaskStatuses(Type TaskType)
|
||||
{
|
||||
return _RunningTasks.Where(t => t.TaskType == TaskType).ToList();
|
||||
}
|
||||
public static List<ScheduledTaskStatus> GetTaskStatuses()
|
||||
{
|
||||
return _RunningTasks.ToList();
|
||||
}
|
||||
|
||||
public static ScheduledTaskStatus RegisterTask(ScheduledTask Task)
|
||||
{
|
||||
return RegisterTask(Task, null);
|
||||
}
|
||||
public static ScheduledTaskStatus RegisterTask(ScheduledTask Task, TriggerBuilder TaskBuilder)
|
||||
{
|
||||
var sessionId = Guid.NewGuid().ToString("D");
|
||||
var triggerKey = GenerateTriggerKey();
|
||||
var taskType = Task.GetType();
|
||||
|
||||
var taskStatus = new ScheduledTaskStatus(Task, sessionId, triggerKey.Name);
|
||||
|
||||
lock (_RunningTasksLock)
|
||||
{
|
||||
if (Task.SingleInstanceTask)
|
||||
{
|
||||
var existingGuid = _RunningTasks.Where(t => t.IsRunning && t.TaskType == taskType).Select(t => t.SessionId).FirstOrDefault();
|
||||
if (existingGuid != null)
|
||||
throw new InvalidOperationException(string.Format("This Single-Instance Task is already running, SessionId: {0}", existingGuid));
|
||||
}
|
||||
_RunningTasks.Add(taskStatus);
|
||||
}
|
||||
|
||||
if (TaskBuilder != null)
|
||||
{
|
||||
ITrigger trigger = TaskBuilder.WithIdentity(triggerKey).Build();
|
||||
IJobDetail jobDetails = new JobDetailImpl(sessionId, taskType)
|
||||
{
|
||||
Description = Task.TaskName,
|
||||
JobDataMap = trigger.JobDataMap
|
||||
};
|
||||
|
||||
_TaskScheduler.ScheduleJob(jobDetails, trigger);
|
||||
|
||||
var nextTriggerTime = trigger.GetNextFireTimeUtc();
|
||||
if (nextTriggerTime.HasValue)
|
||||
taskStatus.SetNextScheduledTimestamp(nextTriggerTime.Value.LocalDateTime);
|
||||
}
|
||||
|
||||
return taskStatus;
|
||||
}
|
||||
public static bool UnregisterTask(this ScheduledTask Task)
|
||||
{
|
||||
lock (_RunningTasksLock)
|
||||
{
|
||||
if (_RunningTasks.Contains(Task.Status))
|
||||
{
|
||||
//_RunningTasks.Remove(Task.Status);
|
||||
_TaskScheduler.UnscheduleJob(Task.ExecutionContext.Trigger.Key);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public static bool UnregisterTask(this ScheduledTaskStatus TaskStatus)
|
||||
{
|
||||
lock (_RunningTasksLock)
|
||||
{
|
||||
if (_RunningTasks.Contains(TaskStatus))
|
||||
{
|
||||
//_RunningTasks.Remove(Task.Status);
|
||||
_TaskScheduler.UnscheduleJob(new TriggerKey(TaskStatus.TriggerKey, SchedulerGroupName));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static TriggerKey GenerateTriggerKey()
|
||||
{
|
||||
return new TriggerKey(Guid.NewGuid().ToString("D"), SchedulerGroupName);
|
||||
}
|
||||
|
||||
public static ScheduledTaskStatus GetDiscoScheduledTaskStatus(this IJobExecutionContext context)
|
||||
{
|
||||
return GetTaskStatus(context.JobDetail.Key.Name);
|
||||
}
|
||||
|
||||
private class ScheduledTaskCleanup : IJob
|
||||
{
|
||||
public void Execute(IJobExecutionContext context)
|
||||
{
|
||||
lock (ScheduledTasks._RunningTasksLock)
|
||||
{
|
||||
// Lifetime = 5mins
|
||||
var expiredTime = DateTime.Now.AddMinutes(-1);
|
||||
var expiredTasks = ScheduledTasks._RunningTasks.Where(
|
||||
t => !t.IsRunning &&
|
||||
!t.NextScheduledTimestamp.HasValue &&
|
||||
t.FinishedTimestamp < expiredTime
|
||||
).ToArray();
|
||||
|
||||
foreach (var expiredTask in expiredTasks)
|
||||
ScheduledTasks._RunningTasks.Remove(expiredTask);
|
||||
}
|
||||
}
|
||||
public static void Schedule(IScheduler TaskScheduler)
|
||||
{
|
||||
// Next 10min interval
|
||||
DateTime now = DateTime.Now;
|
||||
int mins = (10 - (now.Minute % 10));
|
||||
if (mins < 2)
|
||||
mins += 10;
|
||||
DateTimeOffset startAt = new DateTimeOffset(now).AddMinutes(mins).AddSeconds(now.Second * -1).AddMilliseconds(now.Millisecond * -1);
|
||||
ITrigger trigger = TriggerBuilder.Create()
|
||||
.StartAt(startAt)
|
||||
.WithSchedule(SimpleScheduleBuilder.RepeatMinutelyForever(10))
|
||||
.WithIdentity("ScheduledTaskCleanupTrigger", ScheduledTasks.SchedulerGroupName + "_System")
|
||||
.Build();
|
||||
|
||||
IJobDetail job = JobBuilder.Create<ScheduledTaskCleanup>()
|
||||
.WithIdentity("ScheduledTaskCleanupJob", ScheduledTasks.SchedulerGroupName + "_System")
|
||||
.Build();
|
||||
|
||||
_TaskScheduler.ScheduleJob(job, trigger);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user