From 43922eaf202f7b5c84eeb448718d95d6014b6000 Mon Sep 17 00:00:00 2001 From: Daniel Stolt Date: Mon, 3 Aug 2015 15:01:33 +0100 Subject: [PATCH] Extended recipe execution logging. --- .../Recipes/RecipeHandlers/ModuleStepTest.cs | 3 + .../RecipeExecutionStepHandlerTest.cs | 7 ++ .../Recipes/RecipeHandlers/ThemeStepTest.cs | 3 + .../Recipes/Executors/AuditTrailStep.cs | 43 +++++++---- .../Recipes/Executors/FormSubmissionsStep.cs | 40 ++++++---- .../Recipes/Executors/CustomElementsStep.cs | 28 +++++-- .../Recipes/Executors/FeatureStep.cs | 5 +- .../Providers/Executors/CommandStep.cs | 3 +- .../Executors/ContentDefinitionStep.cs | 3 +- .../Providers/Executors/ContentStep.cs | 10 ++- .../Providers/Executors/MigrationStep.cs | 5 +- .../Providers/Executors/ModuleStep.cs | 5 +- .../Providers/Executors/RecipesStep.cs | 28 ++++--- .../Providers/Executors/SettingsStep.cs | 7 +- .../Services/RecipeScheduler.cs | 10 ++- .../Services/RecipeStepExecutor.cs | 4 +- .../Recipes/Executors/RolesStep.cs | 9 ++- .../Recipes/Executors/RulesStep.cs | 49 +++++++----- .../Recipes/Executors/CurrentThemeStep.cs | 14 +++- .../Recipes/Executors/ThemeStep.cs | 3 +- .../Recipes/Executors/WorkflowsStep.cs | 74 +++++++++++-------- src/Orchard/Orchard.Framework.csproj | 1 + .../Recipes/Services/RecipeExecutionLogger.cs | 22 ++++++ .../Recipes/Services/RecipeExecutionStep.cs | 19 ++++- 24 files changed, 275 insertions(+), 120 deletions(-) create mode 100644 src/Orchard/Recipes/Services/RecipeExecutionLogger.cs diff --git a/src/Orchard.Tests.Modules/Recipes/RecipeHandlers/ModuleStepTest.cs b/src/Orchard.Tests.Modules/Recipes/RecipeHandlers/ModuleStepTest.cs index 7e27d24d0..0314bdf25 100644 --- a/src/Orchard.Tests.Modules/Recipes/RecipeHandlers/ModuleStepTest.cs +++ b/src/Orchard.Tests.Modules/Recipes/RecipeHandlers/ModuleStepTest.cs @@ -24,6 +24,7 @@ using Orchard.Packaging.Models; using Orchard.Packaging.Services; using Orchard.Recipes.Models; using Orchard.Recipes.Providers.Executors; +using Orchard.Recipes.Services; using Orchard.Tests.Environment.Extensions; using Orchard.Tests.Environment.Features; using Orchard.Tests.Stubs; @@ -53,6 +54,8 @@ namespace Orchard.Tests.Modules.Recipes.RecipeHandlers { _packagesInRepository = new StubPackagingSourceManager(); _packageManager = new StubPackageManager(); builder.RegisterInstance(_folders).As(); + builder.RegisterType().As(); + builder.RegisterType().AsSelf(); builder.RegisterType().As(); builder.RegisterType().As(); builder.RegisterType().As(); diff --git a/src/Orchard.Tests.Modules/Recipes/RecipeHandlers/RecipeExecutionStepHandlerTest.cs b/src/Orchard.Tests.Modules/Recipes/RecipeHandlers/RecipeExecutionStepHandlerTest.cs index a0d787375..7774e4c20 100644 --- a/src/Orchard.Tests.Modules/Recipes/RecipeHandlers/RecipeExecutionStepHandlerTest.cs +++ b/src/Orchard.Tests.Modules/Recipes/RecipeHandlers/RecipeExecutionStepHandlerTest.cs @@ -4,6 +4,7 @@ using NUnit.Framework; using Orchard.Recipes.Models; using Orchard.Recipes.Providers.RecipeHandlers; using Orchard.Recipes.Services; +using Orchard.Tests.Stubs; namespace Orchard.Tests.Modules.Recipes.RecipeHandlers { [TestFixture] @@ -13,6 +14,8 @@ namespace Orchard.Tests.Modules.Recipes.RecipeHandlers { [SetUp] public void Init() { var builder = new ContainerBuilder(); + builder.RegisterType().As(); + builder.RegisterType().AsSelf(); builder.RegisterType().As().AsSelf().SingleInstance(); builder.RegisterType().SingleInstance(); @@ -37,6 +40,10 @@ namespace Orchard.Tests.Modules.Recipes.RecipeHandlers { } public class StubRecipeExecutionStep : RecipeExecutionStep { + + public StubRecipeExecutionStep(IWorkContextAccessor workContextAccessor) : base(workContextAccessor) { + } + public override string Name { get { return "FakeRecipeStep"; } } diff --git a/src/Orchard.Tests.Modules/Recipes/RecipeHandlers/ThemeStepTest.cs b/src/Orchard.Tests.Modules/Recipes/RecipeHandlers/ThemeStepTest.cs index 4330c7b6f..691c69e01 100644 --- a/src/Orchard.Tests.Modules/Recipes/RecipeHandlers/ThemeStepTest.cs +++ b/src/Orchard.Tests.Modules/Recipes/RecipeHandlers/ThemeStepTest.cs @@ -22,6 +22,7 @@ using Orchard.FileSystems.VirtualPath; using Orchard.Packaging.GalleryServer; using Orchard.Packaging.Services; using Orchard.Recipes.Models; +using Orchard.Recipes.Services; using Orchard.Tests.DisplayManagement.Descriptors; using Orchard.Tests.Environment.Extensions; using Orchard.Tests.Environment.Features; @@ -56,6 +57,8 @@ namespace Orchard.Tests.Modules.Recipes.RecipeHandlers { _packagesInRepository = new ModuleStepTest.StubPackagingSourceManager(); _packageManager = new ModuleStepTest.StubPackageManager(); builder.RegisterInstance(_folders).As(); + builder.RegisterType().As(); + builder.RegisterType().AsSelf(); builder.RegisterType().As(); builder.RegisterType().As(); builder.RegisterType().As(); diff --git a/src/Orchard.Web/Modules/Orchard.AuditTrail/Recipes/Executors/AuditTrailStep.cs b/src/Orchard.Web/Modules/Orchard.AuditTrail/Recipes/Executors/AuditTrailStep.cs index 17fdb8396..e48a50cde 100644 --- a/src/Orchard.Web/Modules/Orchard.AuditTrail/Recipes/Executors/AuditTrailStep.cs +++ b/src/Orchard.Web/Modules/Orchard.AuditTrail/Recipes/Executors/AuditTrailStep.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using Orchard.AuditTrail.Models; using Orchard.ContentManagement; using Orchard.Data; @@ -15,7 +16,11 @@ namespace Orchard.AuditTrail.Recipes.Executors { private readonly IAuthorizer _authorizer; private readonly IWorkContextAccessor _wca; - public AuditTrailStep(IRepository auditTrailEventRepository, IAuthorizer authorizer, IWorkContextAccessor wca) { + public AuditTrailStep( + IRepository auditTrailEventRepository, + IAuthorizer authorizer, + IWorkContextAccessor wca) : base(wca) { + _auditTrailEventRepository = auditTrailEventRepository; _authorizer = authorizer; _wca = wca; @@ -31,20 +36,30 @@ namespace Orchard.AuditTrail.Recipes.Executors { return; } - foreach (var eventElement in context.RecipeStep.Step.Elements()) { - var record = new AuditTrailEventRecord { - EventName = eventElement.Attr("Name"), - FullEventName = eventElement.Attr("FullName"), - Category = eventElement.Attr("Category"), - UserName = eventElement.Attr("User"), - CreatedUtc = eventElement.Attr("CreatedUtc"), - EventFilterKey = eventElement.Attr("EventFilterKey"), - EventFilterData = eventElement.Attr("EventFilterData"), - Comment = eventElement.El("Comment"), - EventData = eventElement.Element("EventData").ToString(), - }; + var elements = context.RecipeStep.Step.Elements().ToArray(); + for (var i = 0; i < elements.Length; i ++) { + var eventElement = elements[i]; + Logger.Information("Importing audit trail event {0}/{1}.", i + 1, elements.Length); - _auditTrailEventRepository.Create(record); + try { + var record = new AuditTrailEventRecord { + EventName = eventElement.Attr("Name"), + FullEventName = eventElement.Attr("FullName"), + Category = eventElement.Attr("Category"), + UserName = eventElement.Attr("User"), + CreatedUtc = eventElement.Attr("CreatedUtc"), + EventFilterKey = eventElement.Attr("EventFilterKey"), + EventFilterData = eventElement.Attr("EventFilterData"), + Comment = eventElement.El("Comment"), + EventData = eventElement.Element("EventData").ToString(), + }; + + _auditTrailEventRepository.Create(record); + } + catch (Exception ex) { + Logger.Error(ex, "Error while importing audit trail event {0}/{1}.", i + 1, elements.Length); + throw; + } } } } diff --git a/src/Orchard.Web/Modules/Orchard.DynamicForms/Recipes/Executors/FormSubmissionsStep.cs b/src/Orchard.Web/Modules/Orchard.DynamicForms/Recipes/Executors/FormSubmissionsStep.cs index d0c7eb5cc..3c979173e 100644 --- a/src/Orchard.Web/Modules/Orchard.DynamicForms/Recipes/Executors/FormSubmissionsStep.cs +++ b/src/Orchard.Web/Modules/Orchard.DynamicForms/Recipes/Executors/FormSubmissionsStep.cs @@ -1,33 +1,47 @@ using System; +using System.Linq; using Orchard.ContentManagement; using Orchard.DynamicForms.Models; using Orchard.DynamicForms.Services; -using Orchard.Environment.Extensions; +using Orchard.Logging; using Orchard.Recipes.Models; using Orchard.Recipes.Services; namespace Orchard.DynamicForms.Recipes.Executors { public class FormSubmissionsStep : RecipeExecutionStep { private readonly IFormService _formService; - public FormSubmissionsStep(IFormService formService) { + + public FormSubmissionsStep(IFormService formService, + IWorkContextAccessor workContextAccessor) : base(workContextAccessor) { + _formService = formService; } public override string Name { get { return "Forms"; } } - public override void Execute(RecipeExecutionContext context) { - var formsElement = context.RecipeStep.Step.Elements(); - foreach (var formElement in formsElement) { - var formName = formElement.Attr("Name"); - var submissionElements = formElement.Element("Submissions").Elements(); - foreach (var submissionElement in submissionElements) { - _formService.CreateSubmission(new Submission { - FormName = formName, - CreatedUtc = submissionElement.Attr("CreatedUtc"), - FormData = submissionElement.Value - }); + public override void Execute(RecipeExecutionContext context) { + var formElements = context.RecipeStep.Step.Elements(); + foreach (var formElement in formElements) { + var formName = formElement.Attr("Name"); + Logger.Information("Importing form '{0}'.", formName); + + try { + var submissionElements = formElement.Element("Submissions").Elements().ToArray(); + for (var i = 0; i < submissionElements.Length; i++) { + Logger.Information("Importing form submission {0}/{1}.", i + 1, submissionElements.Length); + var submissionElement = submissionElements[i]; + _formService.CreateSubmission(new Submission { + FormName = formName, + CreatedUtc = submissionElement.Attr("CreatedUtc"), + FormData = submissionElement.Value + }); + } + } + catch (Exception ex) { + Logger.Error(ex, "Error while importing form '{0}'.", formName); + throw; } } } diff --git a/src/Orchard.Web/Modules/Orchard.Layouts/Recipes/Executors/CustomElementsStep.cs b/src/Orchard.Web/Modules/Orchard.Layouts/Recipes/Executors/CustomElementsStep.cs index 94a14e5ee..fcdc1fc70 100644 --- a/src/Orchard.Web/Modules/Orchard.Layouts/Recipes/Executors/CustomElementsStep.cs +++ b/src/Orchard.Web/Modules/Orchard.Layouts/Recipes/Executors/CustomElementsStep.cs @@ -1,6 +1,8 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using Orchard.Data; using Orchard.Layouts.Models; +using Orchard.Logging; using Orchard.Recipes.Models; using Orchard.Recipes.Services; @@ -8,7 +10,10 @@ namespace Orchard.Layouts.Recipes.Executors { public class CustomElementsStep : RecipeExecutionStep { private readonly IRepository _repository; - public CustomElementsStep(IRepository repository) { + public CustomElementsStep( + IRepository repository, + IWorkContextAccessor workContextAccessor) : base(workContextAccessor) { + _repository = repository; } @@ -23,13 +28,20 @@ namespace Orchard.Layouts.Recipes.Executors { public override void Execute(RecipeExecutionContext context) { foreach (var elementElement in context.RecipeStep.Step.Elements()) { var typeName = elementElement.Attribute("ElementTypeName").Value; - var element = GetOrCreateElement(typeName); + Logger.Information("Importing custom element '{0}'.", typeName); - element.BaseElementTypeName = elementElement.Attribute("BaseElementTypeName").Value; - element.ElementDisplayName = elementElement.Attribute("ElementDisplayName").Value; - element.ElementDescription = elementElement.Attribute("ElementDescription").Value; - element.ElementCategory = elementElement.Attribute("ElementCategory").Value; - element.BaseElementState = elementElement.Element("BaseElementState").Value; + try { + var element = GetOrCreateElement(typeName); + element.BaseElementTypeName = elementElement.Attribute("BaseElementTypeName").Value; + element.ElementDisplayName = elementElement.Attribute("ElementDisplayName").Value; + element.ElementDescription = elementElement.Attribute("ElementDescription").Value; + element.ElementCategory = elementElement.Attribute("ElementCategory").Value; + element.BaseElementState = elementElement.Element("BaseElementState").Value; + } + catch (Exception ex) { + Logger.Error(ex, "Error while importing custom element '{0}'.", typeName); + throw; + } } } diff --git a/src/Orchard.Web/Modules/Orchard.Modules/Recipes/Executors/FeatureStep.cs b/src/Orchard.Web/Modules/Orchard.Modules/Recipes/Executors/FeatureStep.cs index 9e9144cce..95aa6873a 100644 --- a/src/Orchard.Web/Modules/Orchard.Modules/Recipes/Executors/FeatureStep.cs +++ b/src/Orchard.Web/Modules/Orchard.Modules/Recipes/Executors/FeatureStep.cs @@ -10,12 +10,11 @@ namespace Orchard.Modules.Recipes.Executors { public class FeatureStep : RecipeExecutionStep { private readonly IFeatureManager _featureManager; - public FeatureStep(IFeatureManager featureManager) { + public FeatureStep(IFeatureManager featureManager, IWorkContextAccessor workContextAccessor) : base(workContextAccessor) { _featureManager = featureManager; } - public override string Name - { + public override string Name { get { return "Feature"; } } diff --git a/src/Orchard.Web/Modules/Orchard.Recipes/Providers/Executors/CommandStep.cs b/src/Orchard.Web/Modules/Orchard.Recipes/Providers/Executors/CommandStep.cs index f20f41647..f00de2bba 100644 --- a/src/Orchard.Web/Modules/Orchard.Recipes/Providers/Executors/CommandStep.cs +++ b/src/Orchard.Web/Modules/Orchard.Recipes/Providers/Executors/CommandStep.cs @@ -13,7 +13,8 @@ namespace Orchard.Recipes.Providers.Executors { private readonly ICommandManager _commandManager; private readonly CommandParser _commandParser; - public CommandStep(ICommandManager commandManager) { + public CommandStep(ICommandManager commandManager, + IWorkContextAccessor workContextAccessor) : base(workContextAccessor) { _commandManager = commandManager; _commandParser = new CommandParser(); } diff --git a/src/Orchard.Web/Modules/Orchard.Recipes/Providers/Executors/ContentDefinitionStep.cs b/src/Orchard.Web/Modules/Orchard.Recipes/Providers/Executors/ContentDefinitionStep.cs index a31617e81..18d183662 100644 --- a/src/Orchard.Web/Modules/Orchard.Recipes/Providers/Executors/ContentDefinitionStep.cs +++ b/src/Orchard.Web/Modules/Orchard.Recipes/Providers/Executors/ContentDefinitionStep.cs @@ -23,7 +23,8 @@ namespace Orchard.Recipes.Providers.Executors { public ContentDefinitionStep( IContentDefinitionManager contentDefinitionManager, IContentDefinitionReader contentDefinitionReader, - IContentDefinitionEventHandler contentDefinitonEventHandlers) { + IContentDefinitionEventHandler contentDefinitonEventHandlers, + IWorkContextAccessor workContextAccessor) : base(workContextAccessor) { _contentDefinitionManager = contentDefinitionManager; _contentDefinitionReader = contentDefinitionReader; diff --git a/src/Orchard.Web/Modules/Orchard.Recipes/Providers/Executors/ContentStep.cs b/src/Orchard.Web/Modules/Orchard.Recipes/Providers/Executors/ContentStep.cs index 0f5ee6a0f..d808ea363 100644 --- a/src/Orchard.Web/Modules/Orchard.Recipes/Providers/Executors/ContentStep.cs +++ b/src/Orchard.Web/Modules/Orchard.Recipes/Providers/Executors/ContentStep.cs @@ -14,7 +14,11 @@ namespace Orchard.Recipes.Providers.Executors { private readonly IOrchardServices _orchardServices; private readonly ITransactionManager _transactionManager; - public ContentStep(IOrchardServices orchardServices, ITransactionManager transactionManager) { + public ContentStep( + IOrchardServices orchardServices, + ITransactionManager transactionManager, + IWorkContextAccessor workContextAccessor) : base(workContextAccessor) { + _orchardServices = orchardServices; _transactionManager = transactionManager; BatchSize = 64; @@ -79,6 +83,7 @@ namespace Orchard.Recipes.Providers.Executors { // Determine if the import is to be batched in multiple transactions. var startIndex = 0; + var itemIndex = 0; var batchSize = GetBatchSizeForDataStep(context.RecipeStep.Step); Logger.Debug("Using batch size {0}.", batchSize); @@ -96,7 +101,7 @@ namespace Orchard.Recipes.Providers.Executors { if (elementDictionary[nextIdentity.ToString()].HasAttributes) { itemId = elementDictionary[nextIdentity.ToString()].FirstAttribute.Value; } - Logger.Information("Importing data item '{0}'.", itemId); + Logger.Information("Importing data item '{0}' (item {1}/{2}).", itemId, itemIndex + 1, elementDictionary.Count); try { _orchardServices.ContentManager.Import( elementDictionary[nextIdentity.ToString()], @@ -106,6 +111,7 @@ namespace Orchard.Recipes.Providers.Executors { Logger.Error(ex, "Error while importing data item '{0}'.", itemId); throw; } + itemIndex++; nextIdentity = importContentSession.GetNextInBatch(); } diff --git a/src/Orchard.Web/Modules/Orchard.Recipes/Providers/Executors/MigrationStep.cs b/src/Orchard.Web/Modules/Orchard.Recipes/Providers/Executors/MigrationStep.cs index 656d08d7c..9610e28f9 100644 --- a/src/Orchard.Web/Modules/Orchard.Recipes/Providers/Executors/MigrationStep.cs +++ b/src/Orchard.Web/Modules/Orchard.Recipes/Providers/Executors/MigrationStep.cs @@ -10,7 +10,10 @@ namespace Orchard.Recipes.Providers.Executors { public class MigrationStep : RecipeExecutionStep { private readonly IDataMigrationManager _dataMigrationManager; - public MigrationStep(IDataMigrationManager dataMigrationManager) { + public MigrationStep( + IDataMigrationManager dataMigrationManager, + IWorkContextAccessor workContextAccessor) : base(workContextAccessor) { + _dataMigrationManager = dataMigrationManager; } diff --git a/src/Orchard.Web/Modules/Orchard.Recipes/Providers/Executors/ModuleStep.cs b/src/Orchard.Web/Modules/Orchard.Recipes/Providers/Executors/ModuleStep.cs index c18082363..532773fcb 100644 --- a/src/Orchard.Web/Modules/Orchard.Recipes/Providers/Executors/ModuleStep.cs +++ b/src/Orchard.Web/Modules/Orchard.Recipes/Providers/Executors/ModuleStep.cs @@ -18,7 +18,8 @@ namespace Orchard.Recipes.Providers.Executors { public ModuleStep( IPackagingSourceManager packagingSourceManager, IPackageManager packageManager, - IExtensionManager extensionManager) { + IExtensionManager extensionManager, + IWorkContextAccessor workContextAccessor) : base(workContextAccessor) { _packagingSourceManager = packagingSourceManager; _packageManager = packageManager; @@ -47,7 +48,7 @@ namespace Orchard.Recipes.Providers.Executors { } if (packageId == null) { - throw new InvalidOperationException("PackageId is required in a Module declaration in a recipe file."); + throw new InvalidOperationException("PackageId is required in a module declaration in a recipe file."); } // download and install module from the orchard feed or a custom feed if repository is specified. diff --git a/src/Orchard.Web/Modules/Orchard.Recipes/Providers/Executors/RecipesStep.cs b/src/Orchard.Web/Modules/Orchard.Recipes/Providers/Executors/RecipesStep.cs index 819c85533..c3ab89315 100644 --- a/src/Orchard.Web/Modules/Orchard.Recipes/Providers/Executors/RecipesStep.cs +++ b/src/Orchard.Web/Modules/Orchard.Recipes/Providers/Executors/RecipesStep.cs @@ -19,7 +19,8 @@ namespace Orchard.Recipes.Providers.Executors { IRecipeHarvester recipeHarvester, IRecipeStepQueue recipeStepQueue, IRepository recipeStepResultRecordRepository, - ISessionLocator sessionLocator) { + ISessionLocator sessionLocator, + IWorkContextAccessor workContextAccessor) : base(workContextAccessor) { _recipeHarvester = recipeHarvester; _recipeStepQueue = recipeStepQueue; @@ -42,20 +43,23 @@ namespace Orchard.Recipes.Providers.Executors { foreach (var recipeElement in recipeElements) { var extensionId = recipeElement.Attr("ExtensionId"); var recipeName = recipeElement.Attr("Name"); - var recipes = recipesDictionary.ContainsKey(extensionId) ? recipesDictionary[extensionId] : default(IDictionary); - if (recipes == null) { - recipes = recipesDictionary[extensionId] = HarvestRecipes(extensionId); + Logger.Information("Executing recipe '{0}' in extension '{1}'.", recipeName, extensionId); + + try { + var recipes = recipesDictionary.ContainsKey(extensionId) ? recipesDictionary[extensionId] : default(IDictionary); + if (recipes == null) + recipes = recipesDictionary[extensionId] = HarvestRecipes(extensionId); + + if (!recipes.ContainsKey(recipeName)) + throw new Exception(String.Format("No recipe named '{0}' was found in extension '{1}'.", recipeName, extensionId)); + + EnqueueRecipe(session, context.ExecutionId, recipes[recipeName]); } - - var recipe = recipes.ContainsKey(recipeName) ? recipes[recipeName] : default(Recipe); - - if (recipe == null) { - Logger.Error(String.Format("No recipe named {0} was found for extension {1}", recipeName, extensionId)); - continue; + catch (Exception ex) { + Logger.Error(ex, "Error while executing recipe '{0}' in extension '{1}'.", recipeName, extensionId); + throw; } - - EnqueueRecipe(session, context.ExecutionId, recipe); } } diff --git a/src/Orchard.Web/Modules/Orchard.Recipes/Providers/Executors/SettingsStep.cs b/src/Orchard.Web/Modules/Orchard.Recipes/Providers/Executors/SettingsStep.cs index e6c98f0c1..a3f807fbc 100644 --- a/src/Orchard.Web/Modules/Orchard.Recipes/Providers/Executors/SettingsStep.cs +++ b/src/Orchard.Web/Modules/Orchard.Recipes/Providers/Executors/SettingsStep.cs @@ -14,7 +14,12 @@ namespace Orchard.Recipes.Providers.Executors { private readonly IContentManager _contentManager; private readonly Lazy> _handlers; - public SettingsStep(ISiteService siteService, IContentManager contentManager, Lazy> handlers) { + public SettingsStep( + ISiteService siteService, + IContentManager contentManager, + Lazy> handlers, + IWorkContextAccessor workContextAccessor) : base(workContextAccessor) { + _siteService = siteService; _contentManager = contentManager; _handlers = handlers; diff --git a/src/Orchard.Web/Modules/Orchard.Recipes/Services/RecipeScheduler.cs b/src/Orchard.Web/Modules/Orchard.Recipes/Services/RecipeScheduler.cs index 00afb968d..e017aa3ea 100644 --- a/src/Orchard.Web/Modules/Orchard.Recipes/Services/RecipeScheduler.cs +++ b/src/Orchard.Web/Modules/Orchard.Recipes/Services/RecipeScheduler.cs @@ -33,7 +33,6 @@ namespace Orchard.Recipes.Services { public void ScheduleWork(string executionId) { var shellDescriptor = _shellDescriptorManager.GetShellDescriptor(); - Logger.Information("Scheduling execution of recipe {0}.", executionId); // TODO: this task entry may need to become appdata folder backed if it isn't already _processingEngine.AddTask( _shellSettings, @@ -45,15 +44,18 @@ namespace Orchard.Recipes.Services { public void ExecuteWork(string executionId) { ThreadContext.Properties["ExecutionId"] = executionId; try { - Logger.Information("Executing next step of recipe."); // todo: this callback should be guarded against concurrency by the IProcessingEngine var scheduleMore = _recipeStepExecutor.Value.ExecuteNextStep(executionId); - if (scheduleMore) + if (scheduleMore) { + Logger.Information("Scheduling next step of recipe."); ScheduleWork(executionId); - else + } + else { + Logger.Information("All recipe steps executed; restarting shell."); // https://github.com/OrchardCMS/Orchard/issues/3672 // Because recipes execute in their own workcontext, we need to restart the shell, as signaling a cache won't work across workcontexts. _events.Changed(_shellDescriptorManager.GetShellDescriptor(), _shellSettings.Name); + } } finally { ThreadContext.Properties["ExecutionId"] = null; diff --git a/src/Orchard.Web/Modules/Orchard.Recipes/Services/RecipeStepExecutor.cs b/src/Orchard.Web/Modules/Orchard.Recipes/Services/RecipeStepExecutor.cs index 3954ada8e..14cfba976 100644 --- a/src/Orchard.Web/Modules/Orchard.Recipes/Services/RecipeStepExecutor.cs +++ b/src/Orchard.Web/Modules/Orchard.Recipes/Services/RecipeStepExecutor.cs @@ -28,12 +28,12 @@ namespace Orchard.Recipes.Services { public bool ExecuteNextStep(string executionId) { var nextRecipeStep = _recipeStepQueue.Dequeue(executionId); if (nextRecipeStep == null) { - Logger.Information("Recipe execution completed."); + Logger.Information("No more recipe steps left to execute."); _recipeExecuteEventHandler.ExecutionComplete(executionId); return false; } - Logger.Information("Running all recipe handlers for step '{0}'.", nextRecipeStep.Name); + Logger.Information("Executing recipe step '{0}'.", nextRecipeStep.Name); var recipeContext = new RecipeContext { RecipeStep = nextRecipeStep, Executed = false, ExecutionId = executionId }; diff --git a/src/Orchard.Web/Modules/Orchard.Roles/Recipes/Executors/RolesStep.cs b/src/Orchard.Web/Modules/Orchard.Roles/Recipes/Executors/RolesStep.cs index f68d1f528..89f6dcb19 100644 --- a/src/Orchard.Web/Modules/Orchard.Roles/Recipes/Executors/RolesStep.cs +++ b/src/Orchard.Web/Modules/Orchard.Roles/Recipes/Executors/RolesStep.cs @@ -9,7 +9,10 @@ namespace Orchard.Roles.Recipes.Executors { public class RolesStep : RecipeExecutionStep { private readonly IRoleService _roleService; - public RolesStep(IRoleService roleService) { + public RolesStep( + IRoleService roleService, + IWorkContextAccessor workContextAccessor) : base(workContextAccessor) { + _roleService = roleService; } @@ -23,7 +26,7 @@ namespace Orchard.Roles.Recipes.Executors { foreach (var roleElement in context.RecipeStep.Step.Elements()) { var roleName = roleElement.Attribute("Name").Value; - Logger.Information("Processing role '{0}'.", roleName); + Logger.Information("Importing role '{0}'.", roleName); try { var role = _roleService.GetRoleByName(roleName); @@ -40,7 +43,7 @@ namespace Orchard.Roles.Recipes.Executors { _roleService.UpdateRole(role.Id, role.Name, permissionsValid.Union(role.RolesPermissions.Select(p => p.Permission.Name))); } catch (Exception ex) { - Logger.Error(ex, "Error while processing role '{0}'.", roleName); + Logger.Error(ex, "Error while importing role '{0}'.", roleName); throw; } } diff --git a/src/Orchard.Web/Modules/Orchard.Rules/Recipes/Executors/RulesStep.cs b/src/Orchard.Web/Modules/Orchard.Rules/Recipes/Executors/RulesStep.cs index eb0eb5f61..3184f8c1c 100644 --- a/src/Orchard.Web/Modules/Orchard.Rules/Recipes/Executors/RulesStep.cs +++ b/src/Orchard.Web/Modules/Orchard.Rules/Recipes/Executors/RulesStep.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using Orchard.Logging; using Orchard.Recipes.Models; using Orchard.Recipes.Services; using Orchard.Rules.Models; @@ -9,7 +10,10 @@ namespace Orchard.Rules.Recipes.Executors { public class RulesStep : RecipeExecutionStep { private readonly IRulesServices _rulesServices; - public RulesStep(IRulesServices rulesServices) { + public RulesStep( + IRulesServices rulesServices, + IWorkContextAccessor workContextAccessor) : base(workContextAccessor) { + _rulesServices = rulesServices; } @@ -19,27 +23,34 @@ namespace Orchard.Rules.Recipes.Executors { public override void Execute(RecipeExecutionContext context) { foreach (var rule in context.RecipeStep.Step.Elements()) { + var ruleName = rule.Attribute("Name").Value; + Logger.Information("Importing rule '{0}'.", ruleName); - var ruleRecord = _rulesServices.CreateRule(rule.Attribute("Name").Value); - ruleRecord.Enabled = bool.Parse(rule.Attribute("Enabled").Value); + try { + var ruleRecord = _rulesServices.CreateRule(ruleName); + ruleRecord.Enabled = bool.Parse(rule.Attribute("Enabled").Value); - ruleRecord.Actions = rule.Element("Actions").Elements().Select(action => - new ActionRecord { - Type = action.Attribute("Type").Value, - Category = action.Attribute("Category").Value, - Position = int.Parse(action.Attribute("Position").Value), - Parameters = action.Attribute("Parameters").Value, - RuleRecord = ruleRecord - }).ToList(); - - ruleRecord.Events = rule.Element("Events").Elements().Select(action => - new EventRecord { - Type = action.Attribute("Type").Value, - Category = action.Attribute("Category").Value, - Parameters = action.Attribute("Parameters").Value, - RuleRecord = ruleRecord - }).ToList(); + ruleRecord.Actions = rule.Element("Actions").Elements().Select(action => + new ActionRecord { + Type = action.Attribute("Type").Value, + Category = action.Attribute("Category").Value, + Position = int.Parse(action.Attribute("Position").Value), + Parameters = action.Attribute("Parameters").Value, + RuleRecord = ruleRecord + }).ToList(); + ruleRecord.Events = rule.Element("Events").Elements().Select(action => + new EventRecord { + Type = action.Attribute("Type").Value, + Category = action.Attribute("Category").Value, + Parameters = action.Attribute("Parameters").Value, + RuleRecord = ruleRecord + }).ToList(); + } + catch (Exception ex) { + Logger.Error(ex, "Error while importing rule '{0}'.", ruleName); + throw; + } } } } diff --git a/src/Orchard.Web/Modules/Orchard.Themes/Recipes/Executors/CurrentThemeStep.cs b/src/Orchard.Web/Modules/Orchard.Themes/Recipes/Executors/CurrentThemeStep.cs index 106f5ac41..545b4b0be 100644 --- a/src/Orchard.Web/Modules/Orchard.Themes/Recipes/Executors/CurrentThemeStep.cs +++ b/src/Orchard.Web/Modules/Orchard.Themes/Recipes/Executors/CurrentThemeStep.cs @@ -1,3 +1,5 @@ +using System; +using Orchard.Logging; using Orchard.Recipes.Models; using Orchard.Recipes.Services; using Orchard.Themes.Services; @@ -10,13 +12,21 @@ namespace Orchard.Themes.Recipes.Executors { get { return "CurrentTheme"; } } - public CurrentThemeStep(ISiteThemeService siteThemeService) { + public CurrentThemeStep(ISiteThemeService siteThemeService, IWorkContextAccessor workContextAccessor) : base(workContextAccessor) { _siteThemeService = siteThemeService; } public override void Execute(RecipeExecutionContext context) { var themeId = context.RecipeStep.Step.Attribute("id").Value; - _siteThemeService.SetSiteTheme(themeId); + Logger.Information("Setting site theme to '{0}'.", themeId); + + try { + _siteThemeService.SetSiteTheme(themeId); + } + catch (Exception ex) { + Logger.Error(ex, "Error while setting site theme to '{0}'.", themeId); + throw; + } } } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Themes/Recipes/Executors/ThemeStep.cs b/src/Orchard.Web/Modules/Orchard.Themes/Recipes/Executors/ThemeStep.cs index fca8dbced..fe66fc9dc 100644 --- a/src/Orchard.Web/Modules/Orchard.Themes/Recipes/Executors/ThemeStep.cs +++ b/src/Orchard.Web/Modules/Orchard.Themes/Recipes/Executors/ThemeStep.cs @@ -23,7 +23,8 @@ namespace Orchard.Themes.Recipes.Executors { IPackageManager packageManager, IExtensionManager extensionManager, IThemeService themeService, - ISiteThemeService siteThemeService) { + ISiteThemeService siteThemeService, + IWorkContextAccessor workContextAccessor) : base(workContextAccessor) { _packagingSourceManager = packagingSourceManager; _packageManager = packageManager; diff --git a/src/Orchard.Web/Modules/Orchard.Workflows/Recipes/Executors/WorkflowsStep.cs b/src/Orchard.Web/Modules/Orchard.Workflows/Recipes/Executors/WorkflowsStep.cs index 5a4230d0f..b477a4207 100644 --- a/src/Orchard.Web/Modules/Orchard.Workflows/Recipes/Executors/WorkflowsStep.cs +++ b/src/Orchard.Web/Modules/Orchard.Workflows/Recipes/Executors/WorkflowsStep.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using Orchard.Data; +using Orchard.Logging; using Orchard.Recipes.Models; using Orchard.Recipes.Services; using Orchard.Workflows.Models; @@ -14,7 +15,8 @@ namespace Orchard.Workflows.Recipes.Executors { public WorkflowsStep( IRepository workflowDefinitionRepository, IRepository activityRepository, - IRepository transitionRepository) { + IRepository transitionRepository, + IWorkContextAccessor workContextAccessor) : base(workContextAccessor) { _workflowDefinitionRepository = workflowDefinitionRepository; _activityRepository = activityRepository; @@ -27,39 +29,51 @@ namespace Orchard.Workflows.Recipes.Executors { public override void Execute(RecipeExecutionContext context) { foreach (var workflowDefinitionElement in context.RecipeStep.Step.Elements()) { - var workflowDefinition = GetOrCreateWorkflowDefinition(workflowDefinitionElement.Attribute("Name").Value); - var activitiesElement = workflowDefinitionElement.Element("Activities"); - var transitionsElement = workflowDefinitionElement.Element("Transitions"); - var activitiesDictionary = new Dictionary(); + var workflowName = workflowDefinitionElement.Attribute("Name").Value; + Logger.Information("Importing workflow '{0}'.", workflowName); - workflowDefinition.Enabled = Boolean.Parse(workflowDefinitionElement.Attribute("Enabled").Value); + try { + var workflowDefinition = GetOrCreateWorkflowDefinition(workflowName); + var activitiesElement = workflowDefinitionElement.Element("Activities"); + var transitionsElement = workflowDefinitionElement.Element("Transitions"); + var activitiesDictionary = new Dictionary(); - foreach (var activityElement in activitiesElement.Elements()) { - var localId = Int32.Parse(activityElement.Attribute("Id").Value); - var activity = new ActivityRecord { - Name = activityElement.Attribute("Name").Value, - Start = Boolean.Parse(activityElement.Attribute("Start").Value), - X = Int32.Parse(activityElement.Attribute("X").Value), - Y = Int32.Parse(activityElement.Attribute("Y").Value), - State = activityElement.Element("State").Value - }; + workflowDefinition.Enabled = Boolean.Parse(workflowDefinitionElement.Attribute("Enabled").Value); - activitiesDictionary.Add(localId, activity); - workflowDefinition.ActivityRecords.Add(activity); + foreach (var activityElement in activitiesElement.Elements()) { + var localId = Int32.Parse(activityElement.Attribute("Id").Value); + var activityName = activityElement.Attribute("Name").Value; + Logger.Information("Importing activity '{0}' with ID '{1}'.", activityName, localId); + var activity = new ActivityRecord { + Name = activityName, + Start = Boolean.Parse(activityElement.Attribute("Start").Value), + X = Int32.Parse(activityElement.Attribute("X").Value), + Y = Int32.Parse(activityElement.Attribute("Y").Value), + State = activityElement.Element("State").Value + }; + + activitiesDictionary.Add(localId, activity); + workflowDefinition.ActivityRecords.Add(activity); + } + + foreach (var transitionElement in transitionsElement.Elements()) { + var sourceActivityId = Int32.Parse(transitionElement.Attribute("SourceActivityId").Value); + var sourceEndpoint = transitionElement.Attribute("SourceEndpoint").Value; + var destinationActivityId = Int32.Parse(transitionElement.Attribute("DestinationActivityId").Value); + var destinationEndpoint = transitionElement.Attribute("DestinationEndpoint").Value; + Logger.Information("Importing transition between activities '{0}' and '{1}'.", sourceActivityId, destinationActivityId); + + workflowDefinition.TransitionRecords.Add(new TransitionRecord { + SourceActivityRecord = activitiesDictionary[sourceActivityId], + SourceEndpoint = sourceEndpoint, + DestinationActivityRecord = activitiesDictionary[destinationActivityId], + DestinationEndpoint = destinationEndpoint + }); + } } - - foreach (var transitionElement in transitionsElement.Elements()) { - var sourceActivityId = Int32.Parse(transitionElement.Attribute("SourceActivityId").Value); - var sourceEndpoint = transitionElement.Attribute("SourceEndpoint").Value; - var destinationActivityId = Int32.Parse(transitionElement.Attribute("DestinationActivityId").Value); - var destinationEndpoint = transitionElement.Attribute("DestinationEndpoint").Value; - - workflowDefinition.TransitionRecords.Add(new TransitionRecord { - SourceActivityRecord = activitiesDictionary[sourceActivityId], - SourceEndpoint = sourceEndpoint, - DestinationActivityRecord = activitiesDictionary[destinationActivityId], - DestinationEndpoint = destinationEndpoint - }); + catch (Exception ex) { + Logger.Error(ex, "Error while importing workflow '{0}'.", workflowName); + throw; } } } diff --git a/src/Orchard/Orchard.Framework.csproj b/src/Orchard/Orchard.Framework.csproj index 5d687207c..f3d00707d 100644 --- a/src/Orchard/Orchard.Framework.csproj +++ b/src/Orchard/Orchard.Framework.csproj @@ -165,6 +165,7 @@ + diff --git a/src/Orchard/Recipes/Services/RecipeExecutionLogger.cs b/src/Orchard/Recipes/Services/RecipeExecutionLogger.cs new file mode 100644 index 000000000..f27cee9a0 --- /dev/null +++ b/src/Orchard/Recipes/Services/RecipeExecutionLogger.cs @@ -0,0 +1,22 @@ +using System; +using Orchard.Logging; + +namespace Orchard.Recipes.Services { + public class RecipeExecutionLogger : ITransientDependency, ILogger { + public RecipeExecutionLogger() { + Logger = NullLogger.Instance; + } + + public Type ComponentType { get; internal set; } + public ILogger Logger { get; set; } + + public bool IsEnabled(LogLevel level) { + return Logger.IsEnabled(level); + } + + public void Log(LogLevel level, Exception exception, string format, params object[] args) { + var message = String.Format(format, args); + Logger.Log(level, exception, "{0}: {1}", ComponentType.Name, message); + } + } +} diff --git a/src/Orchard/Recipes/Services/RecipeExecutionStep.cs b/src/Orchard/Recipes/Services/RecipeExecutionStep.cs index f8bc7b8d2..5695f9ccb 100644 --- a/src/Orchard/Recipes/Services/RecipeExecutionStep.cs +++ b/src/Orchard/Recipes/Services/RecipeExecutionStep.cs @@ -1,10 +1,23 @@ using System.Collections.Generic; using Orchard.ContentManagement; using Orchard.Localization; +using Orchard.Logging; using Orchard.Recipes.Models; namespace Orchard.Recipes.Services { - public abstract class RecipeExecutionStep : Component, IRecipeExecutionStep { + public abstract class RecipeExecutionStep : IDependency, IRecipeExecutionStep { + private readonly IWorkContextAccessor _workContextAccessor; + private readonly RecipeExecutionLogger _logger; + + public RecipeExecutionStep(IWorkContextAccessor workContextAccessor) { + _workContextAccessor = workContextAccessor; + _logger = _workContextAccessor.GetContext().Resolve(); + _logger.ComponentType = GetType(); + T = NullLocalizer.Instance; + } + + public Localizer T { get; set; } + public abstract string Name { get; } public virtual IEnumerable Names { @@ -23,6 +36,10 @@ namespace Orchard.Recipes.Services { get { return GetType().Name; } } + protected virtual ILogger Logger { + get { return _logger; } + } + public virtual dynamic BuildEditor(dynamic shapeFactory) { return null; }