// Copyright (c) 2012, Outercurve Foundation. // All rights reserved. // // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: // // - Redistributions of source code must retain the above copyright notice, this // list of conditions and the following disclaimer. // // - Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // // - Neither the name of the Outercurve Foundation nor the names of its // contributors may be used to endorse or promote products derived from this // software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON // ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. using System; using System.IO; using System.Threading; using System.Collections; using System.Collections.Generic; using System.Text; using System.Web; using System.Web.Caching; using System.Xml; using System.Reflection; using WebsitePanel.Providers.Common; using System.Diagnostics; using System.Linq; namespace WebsitePanel.EnterpriseServer { public class TaskManager { private static Hashtable eventHandlers = null; // purge timer, used for killing old tasks from the hash static Timer purgeTimer = new Timer(new TimerCallback(PurgeCompletedTasks), null, 60000, // start from 1 minute 60000); // invoke each minute public static void StartTask(string source, string taskName) { StartTask(source, taskName, 0); } public static void StartTask(string source, string taskName, int itemId) { StartTask(source, taskName, 0, new List()); } public static void StartTask(string source, string taskName, int itemId, BackgroundTaskParameter parameter) { StartTask(source, taskName, null, itemId, parameter); } public static void StartTask(string source, string taskName, int itemId, List parameters) { StartTask(source, taskName, null, itemId, parameters); } public static void StartTask(string source, string taskName, object itemName) { StartTask(source, taskName, itemName, 0); } public static void StartTask(string source, string taskName, object itemName, int itemId) { StartTask(source, taskName, itemName, itemId, new List()); } public static void StartTask(string source, string taskName, object itemName, BackgroundTaskParameter parameter) { StartTask(source, taskName, itemName, 0, parameter); } public static void StartTask(string source, string taskName, object itemName, List parameters) { StartTask(source, taskName, itemName, 0, parameters); } public static void StartTask(string source, string taskName, object itemName, int itemId, BackgroundTaskParameter parameter) { StartTask(source, taskName, itemName, itemId, 0, parameter); } public static void StartTask(string source, string taskName, object itemName, int itemId, List parameters) { StartTask(source, taskName, itemName, itemId, 0, 0, -1, parameters); } public static void StartTask(string source, string taskName, object itemName, int itemId, int packageId, BackgroundTaskParameter parameter) { List parameters = new List(); if (parameter != null) { parameters.Add(parameter); } StartTask(source, taskName, itemName, itemId, 0, packageId, -1, parameters); } public static void StartTask(string taskId, string source, string taskName, object itemName, int itemId) { StartTask(taskId, source, taskName, itemName, itemId, 0, 0, -1, new List()); } public static void StartTask(string taskId, string source, string taskName, object itemName, int itemId, int packageId) { StartTask(taskId, source, taskName, itemName, itemId, 0, packageId, -1, new List()); } public static void StartTask(string source, string taskName, object itemName, int itemId, int scheduleId, int packageId, int maximumExecutionTime, List parameters) { StartTask(null, source, taskName, itemName, itemId, scheduleId, packageId, maximumExecutionTime, new List()); } public static void StartTask(string taskId, string source, string taskName, object itemName, int itemId, int scheduleId, int packageId, int maximumExecutionTime, List parameters) { if (String.IsNullOrEmpty(taskId)) { taskId = Guid.NewGuid().ToString("N"); } int userId = SecurityContext.User.UserId; int effectiveUserId = SecurityContext.User.IsPeer ? SecurityContext.User.OwnerId : userId; String itemNameStr = itemName != null ? itemName.ToString() : String.Empty; BackgroundTask task = new BackgroundTask(taskId, userId, effectiveUserId, source, taskName, itemNameStr, itemId, scheduleId, packageId, maximumExecutionTime, parameters); List tasks = TaskController.GetTasks(); if (tasks.Count > 0) { BackgroundTask rootTask = tasks[0]; BackgroundTaskLogRecord log = new BackgroundTaskLogRecord( rootTask.Id, tasks.Count - 1, true, String.Format("{0}_{1}", source, taskName), new string[] {itemNameStr}); TaskController.AddLog(log); } // call event handler CallTaskEventHandler(task, false); TaskController.AddTask(task); } public static void WriteParameter(string parameterName, object parameterValue) { string val = parameterValue != null ? parameterValue.ToString() : ""; WriteLogRecord(0, parameterName + ": " + val, null, null); } public static void Write(string text, params string[] textParameters) { // INFO WriteLogRecord(0, text, null, textParameters); } public static void WriteWarning(string text, params string[] textParameters) { // WARNING WriteLogRecord(1, text, null, textParameters); } public static Exception WriteError(Exception ex) { // ERROR WriteLogRecord(2, ex.Message, ex.StackTrace); BackgroundTask topTask = TaskController.GetTopTask(); return new Exception((topTask != null) ? String.Format("Error executing '{0}' task on '{1}' {2}", topTask.TaskName, topTask.ItemName, topTask.Source) : String.Format("Error executing task"), ex); } public static void WriteError(Exception ex, string text, params string[] textParameters) { // ERROR string[] prms = new string[] { ex.Message }; if (textParameters != null && textParameters.Length > 0) { prms = new string[textParameters.Length + 1]; Array.Copy(textParameters, 0, prms, 1, textParameters.Length); prms[0] = ex.Message; } WriteLogRecord(2, text, ex.Message + "\n" + ex.StackTrace, prms); } public static void WriteError(string text, params string[] textParameters) { // ERROR WriteLogRecord(2, text, null, textParameters); } private static void WriteLogRecord(int severity, string text, string stackTrace, params string[] textParameters) { List tasks = TaskController.GetTasks(); if (tasks.Count > 0) { BackgroundTask rootTask = tasks[0]; BackgroundTaskLogRecord log = new BackgroundTaskLogRecord( rootTask.Id, tasks.Count - 1, false, text, stackTrace, textParameters); TaskController.AddLog(log); if (severity > rootTask.Severity) { rootTask.Severity = severity; TaskController.UpdateTask(rootTask); } } } public static void CompleteTask() { List tasks = TaskController.GetTasks(); if (tasks.Count == 0) return; BackgroundTask topTask = TaskController.GetTopTask(); // call event handler CallTaskEventHandler(topTask, true); // finish task topTask.FinishDate = DateTime.Now; topTask.Completed = true; // write task execution result to database if (tasks.Count == 1) // single task { // unregister task globally // tasks.Remove(TopTask.TaskId); // write to database topTask.Logs = TaskController.GetLogs(topTask.Id, topTask.StartDate); string executionLog = FormatExecutionLog(topTask); UserInfo user = UserController.GetUserInternally(topTask.UserId); string username = user != null ? user.Username : null; AuditLog.AddAuditLogRecord(topTask.TaskId, topTask.Severity, topTask.UserId, username, topTask.PackageId, topTask.ItemId, topTask.ItemName, topTask.StartDate, topTask.FinishDate, topTask.Source, topTask.TaskName, executionLog); } TaskController.UpdateTask(topTask); } public static void UpdateParam(String name, Object value) { BackgroundTask topTask = TaskController.GetTopTask(); if (topTask == null) return; topTask.UpdateParamValue(name, value); TaskController.UpdateTask(topTask); } public static int ItemId { set { BackgroundTask topTask = TaskController.GetTopTask(); if (topTask == null) return; topTask.ItemId = value; TaskController.UpdateTask(topTask); } } public static String ItemName { set { BackgroundTask topTask = TaskController.GetTopTask(); if (topTask == null) return; topTask.ItemName = value; TaskController.UpdateTask(topTask); } } public static void UpdateParams(Hashtable parameters) { BackgroundTask topTask = TaskController.GetTopTask(); if (topTask == null) return; foreach (string key in parameters.Keys) { topTask.UpdateParamValue(key, parameters[key]); } TaskController.UpdateTask(topTask); } static string FormatExecutionLog(BackgroundTask task) { StringWriter sw = new StringWriter(); XmlWriter writer = new XmlTextWriter(sw); writer.WriteStartElement("log"); // parameters writer.WriteStartElement("parameters"); foreach (BackgroundTaskParameter param in task.Params) { writer.WriteStartElement("parameter"); writer.WriteAttributeString("name", param.Name); writer.WriteString(param.Value.ToString()); writer.WriteEndElement(); } writer.WriteEndElement(); // parameters // records writer.WriteStartElement("records"); foreach (BackgroundTaskLogRecord record in task.Logs) { writer.WriteStartElement("record"); writer.WriteAttributeString("severity", record.Severity.ToString()); writer.WriteAttributeString("date", record.Date.ToString(System.Globalization.CultureInfo.InvariantCulture)); writer.WriteAttributeString("ident", record.TextIdent.ToString()); // text writer.WriteElementString("text", record.Text); // text parameters if (record.TextParameters != null && record.TextParameters.Length > 0) { writer.WriteStartElement("textParameters"); foreach (string prm in record.TextParameters) { writer.WriteElementString("value", prm); } writer.WriteEndElement(); // textParameters } // stack trace writer.WriteElementString("stackTrace", record.ExceptionStackTrace); writer.WriteEndElement(); } writer.WriteEndElement(); writer.WriteEndElement(); return sw.ToString(); } static void PurgeCompletedTasks(object obj) { List tasks = TaskController.GetTasks(); foreach (BackgroundTask task in tasks) { if (task.MaximumExecutionTime != -1 && ((TimeSpan) (DateTime.Now - task.StartDate)).TotalSeconds > task.MaximumExecutionTime) { task.Status = BackgroundTaskStatus.Abort; TaskController.UpdateTask(task); } } } public static int IndicatorMaximum { set { BackgroundTask topTask = TaskController.GetTopTask(); topTask.IndicatorMaximum = value; TaskController.UpdateTask(topTask); } } public static int IndicatorCurrent { get { return TaskController.GetTopTask().IndicatorCurrent; } set { BackgroundTask topTask = TaskController.GetTopTask(); topTask.IndicatorCurrent = value; TaskController.UpdateTask(topTask); } } public static int MaximumExecutionTime { get { return TaskController.GetTopTask().MaximumExecutionTime; } set { BackgroundTask topTask = TaskController.GetTopTask(); topTask.MaximumExecutionTime = value; TaskController.UpdateTask(topTask); } } public static bool HasErrors(BackgroundTask task) { return task.Severity == 2; } public static BackgroundTask GetTask(string taskId) { BackgroundTask task = TaskController.GetTask(taskId); if (task == null) return null; return task; } public static BackgroundTask GetTaskWithLogRecords(string taskId, DateTime startLogTime) { BackgroundTask task = GetTask(taskId); if (task == null) return null; task.Logs = TaskController.GetLogs(task.Id, startLogTime); return task; } public static Dictionary GetScheduledTasks() { Dictionary scheduledTasks = new Dictionary(); try { foreach (BackgroundTask task in TaskController.GetTasks()) { if (task.ScheduleId > 0 && !task.Completed && !scheduledTasks.ContainsKey(task.ScheduleId)) scheduledTasks.Add(task.ScheduleId, task); } } catch (Exception) { } return scheduledTasks; } public static void SetTaskNotifyOnComplete(string taskId) { BackgroundTask task = GetTask(taskId); if (task == null) return; task.NotifyOnComplete = true; } public static void StopTask(string taskId) { BackgroundTask task = GetTask(taskId); if (task == null) { return; } task.Status = BackgroundTaskStatus.Abort; StopProcess(task.TaskId); TaskController.UpdateTask(task); } public static void StopTask(BackgroundTask task) { if (task == null) { return; } task.Status = BackgroundTaskStatus.Abort; StopProcess(task.TaskId); TaskController.UpdateTask(task); } private static void StopProcess(string taskId) { var process = Process.GetProcesses().FirstOrDefault( p => p.ProcessName.Equals(taskId, StringComparison.CurrentCultureIgnoreCase)); if (process != null) { process.Kill(); process.WaitForExit(10000); } } public static List GetUserTasks(int userId) { List list = new List(); // try to get user first UserInfo user = UserController.GetUser(userId); if (user == null) return list; // prohibited user // get user tasks foreach (BackgroundTask task in TaskController.GetTasks()) { if (task.EffectiveUserId == userId && !task.Completed) list.Add(task); } return list; } public static List GetUserCompletedTasks(int userId) { return new List(); } public static int GetTasksNumber() { return TaskController.GetTasks().Count; } #region Private Helpers private static void CallTaskEventHandler(BackgroundTask task, bool onComplete) { string[] taskHandlers = GetTaskEventHandlers(task.Source, task.TaskName); if (taskHandlers != null) { foreach (string taskHandler in taskHandlers) { try { Type handlerType = Type.GetType(taskHandler); TaskEventHandler handler = (TaskEventHandler)Activator.CreateInstance(handlerType); if (handler != null) { if (onComplete) handler.OnComplete(); else handler.OnStart(); } } catch (Exception ex) { WriteError(ex, "Error executing task event handler: {0}", ex.Message); } } } } private static string[] GetTaskEventHandlers(string source, string taskName) { // load configuration string appRoot = AppDomain.CurrentDomain.BaseDirectory; string path = Path.Combine(appRoot, "TaskEventHandlers.config"); if (eventHandlers == null) { eventHandlers = Hashtable.Synchronized(new Hashtable()); // load from XML if (File.Exists(path)) { List xmlConfigs = new List(); xmlConfigs.Add(new XmlDocument()); xmlConfigs[0].Load(path); // Lookup for external references first XmlNodeList xmlReferences = xmlConfigs[0].SelectNodes("//reference"); foreach (XmlElement xmlReference in xmlReferences) { string referencePath = Path.Combine(appRoot, xmlReference.GetAttribute("src")); if (File.Exists(referencePath)) { XmlDocument xmldoc = new XmlDocument(); xmldoc.Load(referencePath); xmlConfigs.Add(xmldoc); } } // parse XML foreach (XmlDocument xml in xmlConfigs) { XmlNodeList xmlHandlers = xml.SelectNodes("//handler"); foreach (XmlNode xmlHandler in xmlHandlers) { string keyName = xmlHandler.ParentNode.Attributes["source"].Value + xmlHandler.ParentNode.Attributes["name"].Value; // get handlers collection List taskHandlers = (List)eventHandlers[keyName]; if (taskHandlers == null) { taskHandlers = new List(); eventHandlers[keyName] = taskHandlers; } string handlerType = xmlHandler.Attributes["type"].Value; taskHandlers.Add(handlerType); } } } } string fullTaskName = source + taskName; List handlersList = (List)eventHandlers[fullTaskName]; return handlersList == null ? null : handlersList.ToArray(); } #endregion #region ResultTasks public static void CompleteResultTask(ResultObject res, string errorCode, Exception ex, string errorMessage) { if (res != null) { res.IsSuccess = false; if (!string.IsNullOrEmpty(errorCode)) res.ErrorCodes.Add(errorCode); } if (ex != null) TaskManager.WriteError(ex); if (!string.IsNullOrEmpty(errorMessage)) TaskManager.WriteError(errorMessage); //LogRecord. CompleteTask(); } public static void CompleteResultTask(ResultObject res, string errorCode, Exception ex) { CompleteResultTask(res, errorCode, ex, null); } public static void CompleteResultTask(ResultObject res, string errorCode) { CompleteResultTask(res, errorCode, null, null); } public static void CompleteResultTask(ResultObject res) { CompleteResultTask(res, null); } public static void CompleteResultTask() { CompleteResultTask(null); } public static T StartResultTask(string source, string taskName) where T : ResultObject, new() { StartTask(source, taskName); T res = new T(); res.IsSuccess = true; return res; } public static T StartResultTask(string source, string taskName, object itemName) where T : ResultObject, new() { StartTask(source, taskName, itemName); T res = new T(); res.IsSuccess = true; return res; } public static T StartResultTask(string source, string taskName, object itemName, int packageId) where T : ResultObject, new() { StartTask(source, taskName, itemName, 0, packageId, null); T res = new T(); res.IsSuccess = true; return res; } public static T StartResultTask(string source, string taskName, int packageId) where T : ResultObject, new() { StartTask(source, taskName, null, 0, packageId, null); T res = new T(); res.IsSuccess = true; return res; } public static T StartResultTask(string source, string taskName, int itemId, BackgroundTaskParameter parameter) where T : ResultObject, new() { StartTask(source, taskName, itemId, parameter); T res = new T(); res.IsSuccess = true; return res; } public static T StartResultTask(string source, string taskName, int itemId, List parameters) where T : ResultObject, new() { StartTask(source, taskName, itemId, parameters); T res = new T(); res.IsSuccess = true; return res; } public static T StartResultTask(string source, string taskName, int itemId, object itemName, int packageId) where T : ResultObject, new() { StartTask(source, taskName, itemName, itemId, packageId, null); T res = new T(); res.IsSuccess = true; return res; } public static T StartResultTask(string source, string taskName, Guid taskId, object itemName, int packageId) where T : ResultObject, new() { StartTask(taskId.ToString(), source, taskName, itemName, 0, packageId); T res = new T(); res.IsSuccess = true; return res; } public static T StartResultTask(string source, string taskName, Guid taskId, int itemId, object itemName, int packageId) where T : ResultObject, new() { StartTask(taskId.ToString(), source, taskName, itemName, itemId, packageId); T res = new T(); res.IsSuccess = true; return res; } #endregion } }