using System; using System.Collections.Generic; using Core.Common.Base.Properties; using log4net; namespace Core.Common.Base.Service { /// /// Abstract class that can be used for performing activies (like calculations, data imports, data exports, etc.). /// The regular workflow for completely performing an is: -> . /// can be called for cancelling a running . /// public abstract class Activity { public event EventHandler ProgressChanged; private readonly ILog log = LogManager.GetLogger(typeof(Activity)); private string progressText; /// /// Constructs a new . /// protected Activity() { LogMessages = new List(); } /// /// Gets or sets the name of the . /// public virtual string Name { get; set; } /// /// Gets or sets the of the . /// public ActivityStatus Status { get; protected set; } /// /// Gets or sets the progress text of the . /// listeners are notified when the progress text is set. /// public string ProgressText { get { return progressText; } protected set { progressText = value; OnProgressChanged(); } } /// /// Gets or sets the collection of log messages of the (which is appended while performing the ). /// public IList LogMessages { get; private set; } /// /// This method runs the by sequentially making calls to and . /// public void Run() { Initialize(); if (Status == ActivityStatus.Failed) { log.ErrorFormat(Resources.Activity_Run_Initialization_of_0_has_failed, Name); return; } if (Status == ActivityStatus.Cancelled) { log.WarnFormat(Resources.Activity_Run_Execution_of_0_has_been_canceled, Name); return; } Execute(); if (Status == ActivityStatus.Failed) { log.ErrorFormat(Resources.Activity_Run_Execution_of_0_has_failed, Name); } } /// /// This method cancels a running . /// public void Cancel() { ChangeState(OnCancel, ActivityStatus.Cancelling, ActivityStatus.Cancelled); } /// /// This method finishes an that successfully ran. /// Successfully ran activities can be identified by a not equal to or . /// public void Finish() { if (Status != ActivityStatus.Failed && Status != ActivityStatus.Cancelled) { ChangeState(OnFinish, ActivityStatus.Finishing, ActivityStatus.Finished); } } /// /// This method initializes an . /// protected void Initialize() { ChangeState(OnInitialize, ActivityStatus.Initializing, ActivityStatus.Initialized); } /// /// This method executes an that successfully initialized. /// Successfully ran activities can be identified by a not equal to or . /// protected void Execute() { if (Status == ActivityStatus.Finished || Status == ActivityStatus.Cancelled || Status == ActivityStatus.Failed || Status == ActivityStatus.None) { throw new InvalidOperationException(string.Format(Resources.Activity_Execute_Activity_is_0_Initialize_must_be_called_before_Execute, Status)); } if (Status != ActivityStatus.Executed && Status != ActivityStatus.Initialized) { throw new InvalidOperationException(string.Format(Resources.Activity_Execute_Can_t_call_Execute_for_activity_in_0_state, Status)); } try { Status = ActivityStatus.Executing; OnExecute(); if (Status == ActivityStatus.Failed || Status == ActivityStatus.Cancelled) { // keep this status return; } } catch (Exception e) { Status = ActivityStatus.Failed; log.Error(e.Message); return; } Status = ActivityStatus.Executed; } /// /// Initializes internal state to prepare for execution. After calling this method, /// will be set to if /// no error has occurred. /// /// Set to /// when an error has occurred while performing the initialization. protected abstract void OnInitialize(); /// /// Executes one step. This method will be called multiple times to allow for multiple /// execution steps. After calling this method, will be set to /// if no error has occurred. /// /// /// Set to /// when an error has occurred while executing. /// protected abstract void OnExecute(); /// /// Activity has finished successfully. After calling this method, /// will be set to if no error has occurred. /// protected abstract void OnFinish(); /// /// Cancels current run. will change to /// after this method has been called. /// protected abstract void OnCancel(); private void OnProgressChanged() { if (ProgressChanged != null) { ProgressChanged(this, EventArgs.Empty); } } private void ChangeState(Action transitionAction, ActivityStatus statusBefore, ActivityStatus statusAfter) { try { Status = statusBefore; transitionAction(); if (Status == ActivityStatus.Failed) { return; } } catch (Exception e) { Status = ActivityStatus.Failed; log.Error(e.Message); return; } Status = statusAfter; } } }