using System; using Core.Common.Base.Properties; using log4net; namespace Core.Common.Base.Service { /// /// Defines basic activity which can be executed as part of the workflow. /// /// /// Regular workflow for an activity is: Initialize -> Execute -> Finish -> Cleanup. /// Regular workflow for an activity with an error occurring: [Initialize/Execute/Finish] -> ! Exception / Error ! -> Cleanup. /// Regular workflow for an activity being cancelled: [Initialize/Execute/Finish] -> ! Cancel ! -> Cleanup. /// public abstract class Activity { public event EventHandler ProgressChanged; private readonly ILog log = LogManager.GetLogger(typeof(Activity)); private string progressText; protected Activity() { Log = ""; } public string Log { get; set; } public virtual string Name { get; set; } public ActivityStatus Status { get; protected set; } public string ProgressText { get { return progressText; } protected set { progressText = value; OnProgressChanged(); } } public void Run() { try { Initialize(); if (Status == ActivityStatus.Failed) { throw new Exception(string.Format(Resources.Activity_Run_Initialization_of_0_has_failed, Name)); } if (Status == ActivityStatus.Cancelled) { log.WarnFormat(Resources.Activity_Run_Execution_of_0_has_been_canceled, Name); return; } Execute(); if (Status == ActivityStatus.Failed) { throw new Exception(string.Format(Resources.Activity_Run_Execution_of_0_has_failed, Name)); } } catch (Exception exception) { log.Error(exception.Message); } } public void Cancel() { ChangeState(OnCancel, ActivityStatus.Cancelling, ActivityStatus.Cancelled); } public void Finish() { if (Status != ActivityStatus.Failed && Status != ActivityStatus.Cancelled) { ChangeState(OnFinish, ActivityStatus.Finishing, ActivityStatus.Finished); } } protected void Initialize() { ChangeState(OnInitialize, ActivityStatus.Initializing, ActivityStatus.Initialized); } 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; } private void OnProgressChanged() { if (ProgressChanged != null) { ProgressChanged(this, EventArgs.Empty); } } /// /// 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 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; } } }