Implementation for the recipe scheduler. It's a work item queue.

The recipe manager queues recipe steps as tasks and the scheduler component schedules/executes them in tasks.

--HG--
branch : recipe
This commit is contained in:
Suha Can
2011-02-15 15:42:21 -08:00
parent d3e4c2ad68
commit eb24ea67b0
13 changed files with 142 additions and 18 deletions

View File

@@ -65,6 +65,7 @@ namespace Orchard.Tests.Modules.Recipes.Services {
builder.RegisterType<RecipeHarvester>().As<IRecipeHarvester>();
builder.RegisterType<RecipeStepExecutor>().As<IRecipeStepExecutor>();
builder.RegisterType<RecipeStepQueue>().As<IRecipeStepQueue>().InstancePerLifetimeScope();
builder.RegisterType<StubRecipeScheduler>().As<IRecipeScheduler>();
builder.RegisterType<ExtensionManager>().As<IExtensionManager>();
builder.RegisterType<StubCacheManager>().As<ICacheManager>();
builder.RegisterInstance(_folders).As<IExtensionFolders>();
@@ -139,6 +140,18 @@ namespace Orchard.Tests.Modules.Recipes.Services {
}
}
public class StubRecipeScheduler : IRecipeScheduler {
private readonly IRecipeStepExecutor _recipeStepExecutor;
public StubRecipeScheduler(IRecipeStepExecutor recipeStepExecutor) {
_recipeStepExecutor = recipeStepExecutor;
}
public void ScheduleWork(string executionId) {
while (_recipeStepExecutor.ExecuteNextStep(executionId)) ;
}
}
public class CustomRecipeHandler : IRecipeHandler {
public static string AttributeValue;

View File

@@ -61,8 +61,10 @@
<Compile Include="RecipeHandlers\SettingsRecipeHandler.cs" />
<Compile Include="RecipeHandlers\ThemeRecipeHandler.cs" />
<Compile Include="Services\RecipeHarvester.cs" />
<Compile Include="Services\RecipeJournalManager.cs" />
<Compile Include="Services\RecipeManager.cs" />
<Compile Include="Services\RecipeParser.cs" />
<Compile Include="Services\RecipeScheduler.cs" />
<Compile Include="Services\RecipeStepExecutor.cs" />
<Compile Include="Services\RecipeStepQueue.cs" />
</ItemGroup>

View File

@@ -0,0 +1,53 @@
using System;
using Orchard.FileSystems.Media;
using Orchard.Localization;
using Orchard.Logging;
using Orchard.Recipes.Models;
namespace Orchard.Recipes.Services {
public class RecipeJournalManager : IRecipeJournal {
private readonly IStorageProvider _storageProvider;
public RecipeJournalManager(IStorageProvider storageProvider) {
_storageProvider = storageProvider;
Logger = NullLogger.Instance;
T = NullLocalizer.Instance;
}
public Localizer T { get; set; }
ILogger Logger { get; set; }
public void StartExecution(string executionId) {
throw new NotImplementedException();
}
public void ExecutionComplete(string executionId) {
throw new NotImplementedException();
}
public void ExecutionFailed(string executionId) {
throw new NotImplementedException();
}
public void WriteJournalEntry(string executionId, string message) {
throw new NotImplementedException();
}
public RecipeJournal GetRecipeJournal(string executionId) {
throw new NotImplementedException();
}
private IStorageFile GetJournalFile(string executionId) {
IStorageFile journalFile;
try {
journalFile = _storageProvider.GetFile(executionId);
}
catch (ArgumentException) {
journalFile = _storageProvider.CreateFile(executionId);
}
return journalFile;
}
}
}

View File

@@ -6,11 +6,11 @@ using Orchard.Recipes.Models;
namespace Orchard.Recipes.Services {
public class RecipeManager : IRecipeManager {
private readonly IRecipeStepQueue _recipeStepQueue;
private readonly IRecipeStepExecutor _recipeStepExecutor;
private readonly IRecipeScheduler _recipeScheduler;
public RecipeManager(IRecipeStepQueue recipeStepQueue, IRecipeStepExecutor recipeStepExecutor) {
public RecipeManager(IRecipeStepQueue recipeStepQueue, IRecipeScheduler recipeScheduler) {
_recipeStepQueue = recipeStepQueue;
_recipeStepExecutor = recipeStepExecutor;
_recipeScheduler = recipeScheduler;
Logger = NullLogger.Instance;
T = NullLocalizer.Instance;
@@ -19,20 +19,18 @@ namespace Orchard.Recipes.Services {
public Localizer T { get; set; }
ILogger Logger { get; set; }
public void Execute(Recipe recipe) {
public string Execute(Recipe recipe) {
if (recipe == null)
return;
return null;
var executionId = Guid.NewGuid().ToString("n");
// TODO: Run each step inside a transaction boundary.
foreach (var recipeStep in recipe.RecipeSteps) {
_recipeStepQueue.Enqueue(recipeStep, executionId);
_recipeStepQueue.Enqueue(executionId, recipeStep);
}
_recipeScheduler.ScheduleWork(executionId);
// TODO: figure out shell settings and shell descriptor for processing engine to run under
// Use an event handler instead of directly calling the step executor.
// _processingEngine.AddTask(null, null, "IRecipeStepEvents_DoWork", null);
while (_recipeStepExecutor.ExecuteNextStep()) {}
return executionId;
}
}
}

View File

@@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using Orchard.Environment.Configuration;
using Orchard.Environment.Descriptor;
using Orchard.Environment.State;
using Orchard.Recipes.Events;
namespace Orchard.Recipes.Services {
public class RecipeScheduler : IRecipeScheduler, IRecipeSchedulerEventHandler {
private readonly IProcessingEngine _processingEngine;
private readonly ShellSettings _shellSettings;
private readonly IShellDescriptorManager _shellDescriptorManager;
private readonly Lazy<IRecipeStepExecutor> _recipeStepExecutor;
public RecipeScheduler(
IProcessingEngine processingEngine,
ShellSettings shellSettings,
IShellDescriptorManager shellDescriptorManager,
Lazy<IRecipeStepExecutor> recipeStepExecutor) {
_processingEngine = processingEngine;
_shellSettings = shellSettings;
_shellDescriptorManager = shellDescriptorManager;
_recipeStepExecutor = recipeStepExecutor;
}
public void ScheduleWork(string executionId) {
var shellDescriptor = _shellDescriptorManager.GetShellDescriptor();
// TODO: this task entry may need to become appdata folder backed if it isn't already
_processingEngine.AddTask(
_shellSettings,
shellDescriptor,
"IRecipeSchedulerEventHandler.ExecuteWork",
new Dictionary<string, object> { { "executionId", executionId } });
}
public void ExecuteWork(string executionId) {
// todo: this callback should be guarded against concurrency by the IProcessingEngine
var scheduleMore = _recipeStepExecutor.Value.ExecuteNextStep(executionId);
if (scheduleMore)
ScheduleWork(executionId);
}
}
}

View File

@@ -19,8 +19,8 @@ namespace Orchard.Recipes.Services {
public Localizer T { get; set; }
ILogger Logger { get; set; }
public bool ExecuteNextStep() {
var recipeStepWorkItem = _recipeStepQueue.Dequeue();
public bool ExecuteNextStep(string executionId) {
var recipeStepWorkItem = _recipeStepQueue.Dequeue(executionId);
if (recipeStepWorkItem == null) {
return false;
}

View File

@@ -18,11 +18,11 @@ namespace Orchard.Recipes.Services {
public Localizer T { get; set; }
ILogger Logger { get; set; }
public void Enqueue(RecipeStep step, string executionId) {
public void Enqueue(string executionId, RecipeStep step) {
_stepQueue.Enqueue(new Tuple<RecipeStep, string>(step, executionId));
}
public Tuple<RecipeStep, string> Dequeue() {
public Tuple<RecipeStep, string> Dequeue(string executionId) {
return _stepQueue.Count > 0 ? _stepQueue.Dequeue() : null;
}
}

View File

@@ -191,6 +191,7 @@
<Compile Include="Mvc\ShapeResult.cs" />
<Compile Include="Mvc\Spooling\HtmlStringWriter.cs" />
<Compile Include="Mvc\ViewEngines\Razor\IRazorCompilationEvents.cs" />
<Compile Include="Recipes\Events\IRecipeSchedulerEventHandler.cs" />
<Compile Include="Recipes\Models\Recipe.cs" />
<Compile Include="Recipes\Models\RecipeContext.cs" />
<Compile Include="Recipes\Models\RecipeJournal.cs" />
@@ -200,6 +201,7 @@
<Compile Include="Recipes\Services\IRecipeJournal.cs" />
<Compile Include="Recipes\Services\IRecipeManager.cs" />
<Compile Include="Recipes\Services\IRecipeParser.cs" />
<Compile Include="Recipes\Services\IRecipeScheduler.cs" />
<Compile Include="Recipes\Services\IRecipeStepExecutor.cs" />
<Compile Include="Recipes\Services\IRecipeStepQueue.cs" />
<Compile Include="Security\IEncryptionService.cs" />

View File

@@ -0,0 +1,7 @@
using Orchard.Events;
namespace Orchard.Recipes.Events {
public interface IRecipeSchedulerEventHandler : IEventHandler {
void ExecuteWork(string executionId);
}
}

View File

@@ -2,6 +2,6 @@
namespace Orchard.Recipes.Services {
public interface IRecipeManager : IDependency {
void Execute(Recipe recipe);
string Execute(Recipe recipe);
}
}

View File

@@ -0,0 +1,5 @@
namespace Orchard.Recipes.Services {
public interface IRecipeScheduler : IDependency {
void ScheduleWork(string executionId);
}
}

View File

@@ -1,5 +1,5 @@
namespace Orchard.Recipes.Services {
public interface IRecipeStepExecutor : IDependency {
bool ExecuteNextStep();
bool ExecuteNextStep(string executionId);
}
}

View File

@@ -3,7 +3,7 @@ using Orchard.Recipes.Models;
namespace Orchard.Recipes.Services {
public interface IRecipeStepQueue : ISingletonDependency {
void Enqueue(RecipeStep step, string executionId);
Tuple<RecipeStep, string> Dequeue();
void Enqueue(string executionId, RecipeStep step);
Tuple<RecipeStep, string> Dequeue(string executionId);
}
}