mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-10-21 03:14:10 +08:00
RecipeJournal implementation.
- IStorageProvider based journal implementation for the recipe pipeline. - Was needed as the existing reporting capability was found insufficient. --HG-- branch : recipe
This commit is contained in:
@@ -1,4 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Xml.Linq;
|
||||
using Orchard.FileSystems.Media;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Logging;
|
||||
@@ -7,6 +10,7 @@ using Orchard.Recipes.Models;
|
||||
namespace Orchard.Recipes.Services {
|
||||
public class RecipeJournalManager : IRecipeJournal {
|
||||
private readonly IStorageProvider _storageProvider;
|
||||
private readonly string _recipeJournalFolder = "RecipeJournal" + Path.DirectorySeparatorChar;
|
||||
|
||||
public RecipeJournalManager(IStorageProvider storageProvider) {
|
||||
_storageProvider = storageProvider;
|
||||
@@ -18,36 +22,103 @@ namespace Orchard.Recipes.Services {
|
||||
public Localizer T { get; set; }
|
||||
ILogger Logger { get; set; }
|
||||
|
||||
public void StartExecution(string executionId) {
|
||||
throw new NotImplementedException();
|
||||
public void ExecutionStart(string executionId) {
|
||||
var executionJournal = GetJournalFile(executionId);
|
||||
var xElement = XElement.Parse(ReadJournal(executionJournal));
|
||||
xElement.Element("Status").Value = "Started";
|
||||
WriteJournal(executionJournal, xElement);
|
||||
}
|
||||
|
||||
public void ExecutionComplete(string executionId) {
|
||||
throw new NotImplementedException();
|
||||
var executionJournal = GetJournalFile(executionId);
|
||||
var xElement = XElement.Parse(ReadJournal(executionJournal));
|
||||
xElement.Element("Status").Value = "Complete";
|
||||
WriteJournal(executionJournal, xElement);
|
||||
}
|
||||
|
||||
public void ExecutionFailed(string executionId) {
|
||||
throw new NotImplementedException();
|
||||
var executionJournal = GetJournalFile(executionId);
|
||||
var xElement = XElement.Parse(ReadJournal(executionJournal));
|
||||
xElement.Element("Status").Value = "Failed";
|
||||
WriteJournal(executionJournal, xElement);
|
||||
}
|
||||
|
||||
public void WriteJournalEntry(string executionId, string message) {
|
||||
throw new NotImplementedException();
|
||||
var executionJournal = GetJournalFile(executionId);
|
||||
var xElement = XElement.Parse(ReadJournal(executionJournal));
|
||||
var journalEntry = new XElement("Message", message);
|
||||
xElement.Add(journalEntry);
|
||||
WriteJournal(executionJournal, xElement);
|
||||
}
|
||||
|
||||
public RecipeJournal GetRecipeJournal(string executionId) {
|
||||
throw new NotImplementedException();
|
||||
var executionJournal = GetJournalFile(executionId);
|
||||
var xElement = XElement.Parse(ReadJournal(executionJournal));
|
||||
|
||||
var journal = new RecipeJournal { ExecutionId = executionId };
|
||||
var messages = new List<JournalMessage>();
|
||||
|
||||
journal.Status = ReadStatusFromJournal(xElement);
|
||||
foreach (var message in xElement.Elements("Message")) {
|
||||
messages.Add(new JournalMessage {Message = message.Value});
|
||||
}
|
||||
journal.Messages = messages;
|
||||
|
||||
return journal;
|
||||
}
|
||||
|
||||
public RecipeStatus GetRecipeStatus(string executionId) {
|
||||
var executionJournal = GetJournalFile(executionId);
|
||||
var xElement = XElement.Parse(ReadJournal(executionJournal));
|
||||
|
||||
return ReadStatusFromJournal(xElement);
|
||||
}
|
||||
|
||||
private IStorageFile GetJournalFile(string executionId) {
|
||||
IStorageFile journalFile;
|
||||
var journalPath = Path.Combine(_recipeJournalFolder, executionId);
|
||||
try {
|
||||
journalFile = _storageProvider.GetFile(executionId);
|
||||
_storageProvider.TryCreateFolder(_recipeJournalFolder);
|
||||
journalFile = _storageProvider.GetFile(journalPath);
|
||||
}
|
||||
catch (ArgumentException) {
|
||||
journalFile = _storageProvider.CreateFile(executionId);
|
||||
journalFile = _storageProvider.CreateFile(journalPath);
|
||||
var recipeStepElement = new XElement("RecipeJournal");
|
||||
recipeStepElement.Add(new XElement("Status", "Unknown"));
|
||||
WriteJournal(journalFile, recipeStepElement);
|
||||
}
|
||||
|
||||
return journalFile;
|
||||
}
|
||||
|
||||
private static string ReadJournal(IStorageFile executionJournal) {
|
||||
using (var stream = executionJournal.OpenRead()) {
|
||||
using (var streamReader = new StreamReader(stream)) {
|
||||
return streamReader.ReadToEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void WriteJournal(IStorageFile journalFile, XElement journal) {
|
||||
string content = journal.ToString();
|
||||
using (var stream = journalFile.OpenWrite()) {
|
||||
using (var tw = new StreamWriter(stream)) {
|
||||
tw.Write(content);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static RecipeStatus ReadStatusFromJournal(XElement xElement) {
|
||||
switch (xElement.Element("Status").Value) {
|
||||
case "Started":
|
||||
return RecipeStatus.Started;
|
||||
case "Complete":
|
||||
return RecipeStatus.Complete;
|
||||
case "Failed":
|
||||
return RecipeStatus.Failed;
|
||||
default:
|
||||
return RecipeStatus.Unknown;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -7,10 +7,12 @@ namespace Orchard.Recipes.Services {
|
||||
public class RecipeManager : IRecipeManager {
|
||||
private readonly IRecipeStepQueue _recipeStepQueue;
|
||||
private readonly IRecipeScheduler _recipeScheduler;
|
||||
private readonly IRecipeJournal _recipeJournal;
|
||||
|
||||
public RecipeManager(IRecipeStepQueue recipeStepQueue, IRecipeScheduler recipeScheduler) {
|
||||
public RecipeManager(IRecipeStepQueue recipeStepQueue, IRecipeScheduler recipeScheduler, IRecipeJournal recipeJournal) {
|
||||
_recipeStepQueue = recipeStepQueue;
|
||||
_recipeScheduler = recipeScheduler;
|
||||
_recipeJournal = recipeJournal;
|
||||
|
||||
Logger = NullLogger.Instance;
|
||||
T = NullLocalizer.Instance;
|
||||
@@ -24,7 +26,8 @@ namespace Orchard.Recipes.Services {
|
||||
return null;
|
||||
|
||||
var executionId = Guid.NewGuid().ToString("n");
|
||||
// TODO: Run each step inside a transaction boundary.
|
||||
_recipeJournal.ExecutionStart(executionId);
|
||||
|
||||
foreach (var recipeStep in recipe.RecipeSteps) {
|
||||
_recipeStepQueue.Enqueue(executionId, recipeStep);
|
||||
}
|
||||
|
@@ -7,10 +7,12 @@ using Orchard.Recipes.Models;
|
||||
namespace Orchard.Recipes.Services {
|
||||
public class RecipeStepExecutor : IRecipeStepExecutor {
|
||||
private readonly IRecipeStepQueue _recipeStepQueue;
|
||||
private readonly IRecipeJournal _recipeJournal;
|
||||
private readonly IEnumerable<IRecipeHandler> _recipeHandlers;
|
||||
|
||||
public RecipeStepExecutor(IRecipeStepQueue recipeStepQueue, IEnumerable<IRecipeHandler> recipeHandlers) {
|
||||
public RecipeStepExecutor(IRecipeStepQueue recipeStepQueue, IRecipeJournal recipeJournal, IEnumerable<IRecipeHandler> recipeHandlers) {
|
||||
_recipeStepQueue = recipeStepQueue;
|
||||
_recipeJournal = recipeJournal;
|
||||
_recipeHandlers = recipeHandlers;
|
||||
|
||||
Logger = NullLogger.Instance;
|
||||
@@ -23,8 +25,10 @@ namespace Orchard.Recipes.Services {
|
||||
public bool ExecuteNextStep(string executionId) {
|
||||
var nextRecipeStep= _recipeStepQueue.Dequeue(executionId);
|
||||
if (nextRecipeStep == null) {
|
||||
_recipeJournal.ExecutionComplete(executionId);
|
||||
return false;
|
||||
}
|
||||
_recipeJournal.WriteJournalEntry(executionId, string.Format("Executing step {0}.", nextRecipeStep.Name));
|
||||
var recipeContext = new RecipeContext { RecipeStep = nextRecipeStep, Executed = false };
|
||||
try {
|
||||
foreach (var recipeHandler in _recipeHandlers) {
|
||||
@@ -34,11 +38,15 @@ namespace Orchard.Recipes.Services {
|
||||
catch(Exception exception) {
|
||||
Logger.Error(exception, "Recipe execution {0} was cancelled because a step failed to execute", executionId);
|
||||
while (_recipeStepQueue.Dequeue(executionId) != null) ;
|
||||
_recipeJournal.ExecutionFailed(executionId);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!recipeContext.Executed) {
|
||||
Logger.Error("Could not execute recipe step '{0}' because the recipe handler was not found.", recipeContext.RecipeStep.Name);
|
||||
while (_recipeStepQueue.Dequeue(executionId) != null) ;
|
||||
_recipeJournal.ExecutionFailed(executionId);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
Reference in New Issue
Block a user