diff options
| author | LukePulverenti <luke.pulverenti@gmail.com> | 2013-02-24 16:53:54 -0500 |
|---|---|---|
| committer | LukePulverenti <luke.pulverenti@gmail.com> | 2013-02-24 16:53:54 -0500 |
| commit | 8ce3e74e8112a94773df22827849bf274fc88198 (patch) | |
| tree | a4ce1edf34466be697e2e432609f6be80b6c6df6 /MediaBrowser.Common.Implementations/ScheduledTasks | |
| parent | 6c86721f6de2acbe68e9419064ff21111ff3a223 (diff) | |
More DI
Diffstat (limited to 'MediaBrowser.Common.Implementations/ScheduledTasks')
5 files changed, 741 insertions, 0 deletions
diff --git a/MediaBrowser.Common.Implementations/ScheduledTasks/TaskManager.cs b/MediaBrowser.Common.Implementations/ScheduledTasks/TaskManager.cs new file mode 100644 index 000000000..c6eca29d1 --- /dev/null +++ b/MediaBrowser.Common.Implementations/ScheduledTasks/TaskManager.cs @@ -0,0 +1,322 @@ +using MediaBrowser.Common.Kernel; +using MediaBrowser.Common.ScheduledTasks; +using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Serialization; +using MediaBrowser.Model.Tasks; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace MediaBrowser.Common.Implementations.ScheduledTasks +{ + /// <summary> + /// Class TaskManager + /// </summary> + public class TaskManager : ITaskManager + { + /// <summary> + /// Gets the list of Scheduled Tasks + /// </summary> + /// <value>The scheduled tasks.</value> + public IScheduledTask[] ScheduledTasks { get; private set; } + + /// <summary> + /// The _task queue + /// </summary> + private readonly List<Type> _taskQueue = new List<Type>(); + + /// <summary> + /// The _logger + /// </summary> + private readonly ILogger _logger; + + /// <summary> + /// The _application paths + /// </summary> + private readonly IApplicationPaths _applicationPaths; + + /// <summary> + /// The _json serializer + /// </summary> + private readonly IJsonSerializer _jsonSerializer; + + /// <summary> + /// Initializes a new instance of the <see cref="TaskManager" /> class. + /// </summary> + /// <param name="applicationPaths">The application paths.</param> + /// <param name="jsonSerializer">The json serializer.</param> + /// <param name="logger">The logger.</param> + /// <exception cref="System.ArgumentException">kernel</exception> + public TaskManager(IApplicationPaths applicationPaths, IJsonSerializer jsonSerializer, ILogger logger) + { + if (applicationPaths == null) + { + throw new ArgumentException("applicationPaths"); + } + if (jsonSerializer == null) + { + throw new ArgumentException("jsonSerializer"); + } + if (logger == null) + { + throw new ArgumentException("logger"); + } + + _applicationPaths = applicationPaths; + _jsonSerializer = jsonSerializer; + _logger = logger; + + ScheduledTasks = new IScheduledTask[] {}; + } + + /// <summary> + /// Cancels if running and queue. + /// </summary> + /// <typeparam name="T"></typeparam> + public void CancelIfRunningAndQueue<T>() + where T : IScheduledTask + { + ScheduledTasks.OfType<T>().First().CancelIfRunning(); + QueueScheduledTask<T>(); + } + + /// <summary> + /// Queues the scheduled task. + /// </summary> + /// <typeparam name="T"></typeparam> + public void QueueScheduledTask<T>() + where T : IScheduledTask + { + var scheduledTask = ScheduledTasks.OfType<T>().First(); + + QueueScheduledTask(scheduledTask); + } + + /// <summary> + /// Queues the scheduled task. + /// </summary> + /// <param name="task">The task.</param> + public void QueueScheduledTask(IScheduledTask task) + { + var type = task.GetType(); + + var scheduledTask = ScheduledTasks.First(t => t.GetType() == type); + + lock (_taskQueue) + { + // If it's idle just execute immediately + if (scheduledTask.State == TaskState.Idle) + { + scheduledTask.Execute(); + return; + } + + if (!_taskQueue.Contains(type)) + { + _logger.Info("Queueing task {0}", type.Name); + _taskQueue.Add(type); + } + else + { + _logger.Info("Task already queued: {0}", type.Name); + } + } + } + + /// <summary> + /// Called when [task completed]. + /// </summary> + /// <param name="task">The task.</param> + public void OnTaskCompleted(IScheduledTask task) + { + // Execute queued tasks + lock (_taskQueue) + { + var copy = _taskQueue.ToList(); + + foreach (var type in copy) + { + var scheduledTask = ScheduledTasks.First(t => t.GetType() == type); + + if (scheduledTask.State == TaskState.Idle) + { + scheduledTask.Execute(); + + _taskQueue.Remove(type); + } + } + } + } + + /// <summary> + /// Adds the tasks. + /// </summary> + /// <param name="tasks">The tasks.</param> + public void AddTasks(IEnumerable<IScheduledTask> tasks) + { + var myTasks = ScheduledTasks.ToList(); + + myTasks.AddRange(tasks); + + ScheduledTasks = myTasks.ToArray(); + } + + /// <summary> + /// The _scheduled tasks configuration directory + /// </summary> + private string _scheduledTasksConfigurationDirectory; + /// <summary> + /// Gets the scheduled tasks configuration directory. + /// </summary> + /// <value>The scheduled tasks configuration directory.</value> + private string ScheduledTasksConfigurationDirectory + { + get + { + if (_scheduledTasksConfigurationDirectory == null) + { + _scheduledTasksConfigurationDirectory = Path.Combine(_applicationPaths.ConfigurationDirectoryPath, "ScheduledTasks"); + + if (!Directory.Exists(_scheduledTasksConfigurationDirectory)) + { + Directory.CreateDirectory(_scheduledTasksConfigurationDirectory); + } + } + return _scheduledTasksConfigurationDirectory; + } + } + + /// <summary> + /// The _scheduled tasks data directory + /// </summary> + private string _scheduledTasksDataDirectory; + /// <summary> + /// Gets the scheduled tasks data directory. + /// </summary> + /// <value>The scheduled tasks data directory.</value> + private string ScheduledTasksDataDirectory + { + get + { + if (_scheduledTasksDataDirectory == null) + { + _scheduledTasksDataDirectory = Path.Combine(_applicationPaths.DataPath, "ScheduledTasks"); + + if (!Directory.Exists(_scheduledTasksDataDirectory)) + { + Directory.CreateDirectory(_scheduledTasksDataDirectory); + } + } + return _scheduledTasksDataDirectory; + } + } + + /// <summary> + /// Gets the history file path. + /// </summary> + /// <value>The history file path.</value> + private string GetHistoryFilePath(IScheduledTask task) + { + return Path.Combine(ScheduledTasksDataDirectory, task.Id + ".js"); + } + + /// <summary> + /// Gets the configuration file path. + /// </summary> + /// <param name="task">The task.</param> + /// <returns>System.String.</returns> + private string GetConfigurationFilePath(IScheduledTask task) + { + return Path.Combine(ScheduledTasksConfigurationDirectory, task.Id + ".js"); + } + + /// <summary> + /// Called when [task completed]. + /// </summary> + /// <param name="task">The task.</param> + /// <param name="startTime">The start time.</param> + /// <param name="endTime">The end time.</param> + /// <param name="status">The status.</param> + public void OnTaskCompleted(IScheduledTask task, DateTime startTime, DateTime endTime, TaskCompletionStatus status) + { + var elapsedTime = endTime - startTime; + + _logger.Info("{0} {1} after {2} minute(s) and {3} seconds", task.Name, status, Math.Truncate(elapsedTime.TotalMinutes), elapsedTime.Seconds); + + var result = new TaskResult + { + StartTimeUtc = startTime, + EndTimeUtc = endTime, + Status = status, + Name = task.Name, + Id = task.Id + }; + + _jsonSerializer.SerializeToFile(result, GetHistoryFilePath(task)); + + //task.LastExecutionResult = result; + } + + /// <summary> + /// Gets the last execution result. + /// </summary> + /// <param name="task">The task.</param> + /// <returns>TaskResult.</returns> + public TaskResult GetLastExecutionResult(IScheduledTask task) + { + return _jsonSerializer.DeserializeFromFile<TaskResult>(GetHistoryFilePath(task)); + } + + /// <summary> + /// Loads the triggers. + /// </summary> + /// <param name="task">The task.</param> + /// <returns>IEnumerable{BaseTaskTrigger}.</returns> + public IEnumerable<ITaskTrigger> LoadTriggers(IScheduledTask task) + { + try + { + return _jsonSerializer.DeserializeFromFile<IEnumerable<TaskTriggerInfo>>(GetConfigurationFilePath(task)) + .Select(ScheduledTaskHelpers.GetTrigger) + .ToList(); + } + catch (IOException) + { + // File doesn't exist. No biggie. Return defaults. + return task.GetDefaultTriggers(); + } + } + + /// <summary> + /// Saves the triggers. + /// </summary> + /// <param name="task">The task.</param> + /// <param name="triggers">The triggers.</param> + public void SaveTriggers(IScheduledTask task, IEnumerable<ITaskTrigger> triggers) + { + _jsonSerializer.SerializeToFile(triggers.Select(ScheduledTaskHelpers.GetTriggerInfo), GetConfigurationFilePath(task)); + } + + /// <summary> + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// </summary> + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// <summary> + /// Releases unmanaged and - optionally - managed resources. + /// </summary> + /// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param> + protected virtual void Dispose(bool dispose) + { + foreach (var task in ScheduledTasks) + { + task.Dispose(); + } + } + } +} diff --git a/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs b/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs new file mode 100644 index 000000000..2ef056658 --- /dev/null +++ b/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs @@ -0,0 +1,119 @@ +using MediaBrowser.Common.Kernel; +using MediaBrowser.Common.ScheduledTasks; +using MediaBrowser.Model.Logging; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks +{ + /// <summary> + /// Deletes old cache files + /// </summary> + public class DeleteCacheFileTask : BaseScheduledTask<IKernel> + { + /// <summary> + /// Initializes a new instance of the <see cref="DeleteCacheFileTask" /> class. + /// </summary> + /// <param name="kernel">The kernel.</param> + /// <param name="taskManager">The task manager.</param> + /// <param name="logger">The logger.</param> + public DeleteCacheFileTask(IKernel kernel, ITaskManager taskManager, ILogger logger) + : base(kernel, taskManager, logger) + { + } + + /// <summary> + /// Creates the triggers that define when the task will run + /// </summary> + /// <returns>IEnumerable{BaseTaskTrigger}.</returns> + public override IEnumerable<ITaskTrigger> GetDefaultTriggers() + { + var trigger = new DailyTrigger { TimeOfDay = TimeSpan.FromHours(2) }; //2am + + return new[] { trigger }; + } + + /// <summary> + /// Returns the task to be executed + /// </summary> + /// <param name="cancellationToken">The cancellation token.</param> + /// <param name="progress">The progress.</param> + /// <returns>Task.</returns> + protected override Task ExecuteInternal(CancellationToken cancellationToken, IProgress<double> progress) + { + return Task.Run(() => + { + var minDateModified = DateTime.UtcNow.AddMonths(-2); + + DeleteCacheFilesFromDirectory(cancellationToken, Kernel.ApplicationPaths.CachePath, minDateModified, progress); + }); + } + + + /// <summary> + /// Deletes the cache files from directory with a last write time less than a given date + /// </summary> + /// <param name="cancellationToken">The task cancellation token.</param> + /// <param name="directory">The directory.</param> + /// <param name="minDateModified">The min date modified.</param> + /// <param name="progress">The progress.</param> + private void DeleteCacheFilesFromDirectory(CancellationToken cancellationToken, string directory, DateTime minDateModified, IProgress<double> progress) + { + var filesToDelete = new DirectoryInfo(directory).EnumerateFileSystemInfos("*", SearchOption.AllDirectories) + .Where(f => !f.Attributes.HasFlag(FileAttributes.Directory) && f.LastWriteTimeUtc < minDateModified) + .ToList(); + + var index = 0; + + foreach (var file in filesToDelete) + { + double percent = index; + percent /= filesToDelete.Count; + + progress.Report(100 * percent); + + cancellationToken.ThrowIfCancellationRequested(); + + File.Delete(file.FullName); + + index++; + } + + progress.Report(100); + } + + /// <summary> + /// Gets the name of the task + /// </summary> + /// <value>The name.</value> + public override string Name + { + get { return "Cache file cleanup"; } + } + + /// <summary> + /// Gets the description. + /// </summary> + /// <value>The description.</value> + public override string Description + { + get { return "Deletes cache files no longer needed by the system"; } + } + + /// <summary> + /// Gets the category. + /// </summary> + /// <value>The category.</value> + public override string Category + { + get + { + return "Maintenance"; + } + } + } +} diff --git a/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs b/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs new file mode 100644 index 000000000..dd00a7148 --- /dev/null +++ b/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs @@ -0,0 +1,107 @@ +using MediaBrowser.Common.Kernel; +using MediaBrowser.Common.ScheduledTasks; +using MediaBrowser.Model.Logging; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks +{ + /// <summary> + /// Deletes old log files + /// </summary> + public class DeleteLogFileTask : BaseScheduledTask<IKernel> + { + /// <summary> + /// Initializes a new instance of the <see cref="DeleteLogFileTask" /> class. + /// </summary> + /// <param name="kernel">The kernel.</param> + /// <param name="taskManager">The task manager.</param> + /// <param name="logger">The logger.</param> + public DeleteLogFileTask(IKernel kernel, ITaskManager taskManager, ILogger logger) + : base(kernel, taskManager, logger) + { + } + + /// <summary> + /// Creates the triggers that define when the task will run + /// </summary> + /// <returns>IEnumerable{BaseTaskTrigger}.</returns> + public override IEnumerable<ITaskTrigger> GetDefaultTriggers() + { + var trigger = new DailyTrigger { TimeOfDay = TimeSpan.FromHours(2) }; //2am + + return new[] { trigger }; + } + + /// <summary> + /// Returns the task to be executed + /// </summary> + /// <param name="cancellationToken">The cancellation token.</param> + /// <param name="progress">The progress.</param> + /// <returns>Task.</returns> + protected override Task ExecuteInternal(CancellationToken cancellationToken, IProgress<double> progress) + { + return Task.Run(() => + { + // Delete log files more than n days old + var minDateModified = DateTime.UtcNow.AddDays(-(Kernel.Configuration.LogFileRetentionDays)); + + var filesToDelete = new DirectoryInfo(Kernel.ApplicationPaths.LogDirectoryPath).EnumerateFileSystemInfos("*", SearchOption.AllDirectories) + .Where(f => f.LastWriteTimeUtc < minDateModified) + .ToList(); + + var index = 0; + + foreach (var file in filesToDelete) + { + double percent = index; + percent /= filesToDelete.Count; + + progress.Report(100 * percent); + + cancellationToken.ThrowIfCancellationRequested(); + + File.Delete(file.FullName); + + index++; + } + + progress.Report(100); + }); + } + + /// <summary> + /// Gets the name of the task + /// </summary> + /// <value>The name.</value> + public override string Name + { + get { return "Log file cleanup"; } + } + + /// <summary> + /// Gets the description. + /// </summary> + /// <value>The description.</value> + public override string Description + { + get { return string.Format("Deletes log files that are more than {0} days old.", Kernel.Configuration.LogFileRetentionDays); } + } + + /// <summary> + /// Gets the category. + /// </summary> + /// <value>The category.</value> + public override string Category + { + get + { + return "Maintenance"; + } + } + } +} diff --git a/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/ReloadLoggerTask.cs b/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/ReloadLoggerTask.cs new file mode 100644 index 000000000..79c633c76 --- /dev/null +++ b/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/ReloadLoggerTask.cs @@ -0,0 +1,71 @@ +using MediaBrowser.Common.Kernel; +using MediaBrowser.Common.ScheduledTasks; +using MediaBrowser.Model.Logging; +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks +{ + /// <summary> + /// Class ReloadLoggerFileTask + /// </summary> + public class ReloadLoggerFileTask : BaseScheduledTask<IKernel> + { + /// <summary> + /// Initializes a new instance of the <see cref="ReloadLoggerFileTask" /> class. + /// </summary> + /// <param name="kernel">The kernel.</param> + /// <param name="taskManager">The task manager.</param> + /// <param name="logger">The logger.</param> + public ReloadLoggerFileTask(IKernel kernel, ITaskManager taskManager, ILogger logger) + : base(kernel, taskManager, logger) + { + } + + /// <summary> + /// Gets the default triggers. + /// </summary> + /// <returns>IEnumerable{BaseTaskTrigger}.</returns> + public override IEnumerable<ITaskTrigger> GetDefaultTriggers() + { + var trigger = new DailyTrigger { TimeOfDay = TimeSpan.FromHours(0) }; //12am + + return new[] { trigger }; + } + + /// <summary> + /// Executes the internal. + /// </summary> + /// <param name="cancellationToken">The cancellation token.</param> + /// <param name="progress">The progress.</param> + /// <returns>Task.</returns> + protected override Task ExecuteInternal(CancellationToken cancellationToken, IProgress<double> progress) + { + cancellationToken.ThrowIfCancellationRequested(); + + progress.Report(0); + + return Task.Run(() => Kernel.ReloadLogger()); + } + + /// <summary> + /// Gets the name. + /// </summary> + /// <value>The name.</value> + public override string Name + { + get { return "Start new log file"; } + } + + /// <summary> + /// Gets the description. + /// </summary> + /// <value>The description.</value> + public override string Description + { + get { return "Moves logging to a new file to help reduce log file sizes."; } + } + } +} diff --git a/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/SystemUpdateTask.cs b/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/SystemUpdateTask.cs new file mode 100644 index 000000000..a101ad3dd --- /dev/null +++ b/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/SystemUpdateTask.cs @@ -0,0 +1,122 @@ +using MediaBrowser.Common.Kernel; +using MediaBrowser.Common.ScheduledTasks; +using MediaBrowser.Model.Logging; +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks +{ + /// <summary> + /// Plugin Update Task + /// </summary> + public class SystemUpdateTask : BaseScheduledTask<IKernel> + { + /// <summary> + /// The _app host + /// </summary> + private readonly IApplicationHost _appHost; + + /// <summary> + /// Initializes a new instance of the <see cref="SystemUpdateTask" /> class. + /// </summary> + /// <param name="appHost">The app host.</param> + /// <param name="taskManager">The task manager.</param> + /// <param name="kernel">The kernel.</param> + /// <param name="logger">The logger.</param> + public SystemUpdateTask(IApplicationHost appHost, ITaskManager taskManager, IKernel kernel, ILogger logger) + : base(kernel, taskManager, logger) + { + _appHost = appHost; + } + + /// <summary> + /// Creates the triggers that define when the task will run + /// </summary> + /// <returns>IEnumerable{BaseTaskTrigger}.</returns> + public override IEnumerable<ITaskTrigger> GetDefaultTriggers() + { + return new ITaskTrigger[] { + + // 1am + new DailyTrigger { TimeOfDay = TimeSpan.FromHours(1) }, + + new IntervalTrigger { Interval = TimeSpan.FromHours(2)} + }; + } + + /// <summary> + /// Returns the task to be executed + /// </summary> + /// <param name="cancellationToken">The cancellation token.</param> + /// <param name="progress">The progress.</param> + /// <returns>Task.</returns> + protected override async Task ExecuteInternal(CancellationToken cancellationToken, IProgress<double> progress) + { + if (!_appHost.CanSelfUpdate) return; + + EventHandler<double> innerProgressHandler = (sender, e) => progress.Report(e * .1); + + // Create a progress object for the update check + var innerProgress = new Progress<double>(); + innerProgress.ProgressChanged += innerProgressHandler; + + var updateInfo = await _appHost.CheckForApplicationUpdate(cancellationToken, innerProgress).ConfigureAwait(false); + + // Release the event handler + innerProgress.ProgressChanged -= innerProgressHandler; + + progress.Report(10); + + if (!updateInfo.IsUpdateAvailable) + { + progress.Report(100); + return; + } + + cancellationToken.ThrowIfCancellationRequested(); + + if (Kernel.Configuration.EnableAutoUpdate) + { + Logger.Info("Update Revision {0} available. Updating...", updateInfo.AvailableVersion); + + innerProgressHandler = (sender, e) => progress.Report((e * .9) + .1); + + innerProgress = new Progress<double>(); + innerProgress.ProgressChanged += innerProgressHandler; + + await _appHost.UpdateApplication(cancellationToken, innerProgress).ConfigureAwait(false); + + // Release the event handler + innerProgress.ProgressChanged -= innerProgressHandler; + + Kernel.OnApplicationUpdated(updateInfo.AvailableVersion); + } + else + { + Logger.Info("A new version of Media Browser is available."); + } + + progress.Report(100); + } + + /// <summary> + /// Gets the name of the task + /// </summary> + /// <value>The name.</value> + public override string Name + { + get { return "Check for application updates"; } + } + + /// <summary> + /// Gets the description. + /// </summary> + /// <value>The description.</value> + public override string Description + { + get { return "Downloads and installs application updates."; } + } + } +} |
