diff --git a/NzbDrone.Api/Commands/CommandModule.cs b/NzbDrone.Api/Commands/CommandModule.cs
index 371483cc3..beb6fe246 100644
--- a/NzbDrone.Api/Commands/CommandModule.cs
+++ b/NzbDrone.Api/Commands/CommandModule.cs
@@ -4,6 +4,7 @@ using Nancy;
using NzbDrone.Api.Extensions;
using NzbDrone.Common.Composition;
using NzbDrone.Common.Messaging;
+using NzbDrone.Common.Messaging.Manager;
namespace NzbDrone.Api.Commands
{
@@ -11,14 +12,16 @@ namespace NzbDrone.Api.Commands
{
private readonly IMessageAggregator _messageAggregator;
private readonly IContainer _container;
+ private readonly IManageCommands _commandManager;
- public CommandModule(IMessageAggregator messageAggregator, IContainer container)
+ public CommandModule(IMessageAggregator messageAggregator, IContainer container, IManageCommands commandManager)
{
_messageAggregator = messageAggregator;
_container = container;
+ _commandManager = commandManager;
Post["/"] = x => RunCommand(ReadResourceFromRequest());
-
+ Get["/"] = x => GetAllCommands();
}
private Response RunCommand(CommandResource resource)
@@ -33,5 +36,10 @@ namespace NzbDrone.Api.Commands
return resource.AsResponse(HttpStatusCode.Created);
}
+
+ private Response GetAllCommands()
+ {
+ return _commandManager.Items.AsResponse();
+ }
}
}
\ No newline at end of file
diff --git a/NzbDrone.Common.Test/MessagingTests/CommandEqualityComparerFixture.cs b/NzbDrone.Common.Test/MessagingTests/CommandEqualityComparerFixture.cs
new file mode 100644
index 000000000..2b2deadea
--- /dev/null
+++ b/NzbDrone.Common.Test/MessagingTests/CommandEqualityComparerFixture.cs
@@ -0,0 +1,76 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using FluentAssertions;
+using NUnit.Framework;
+using NzbDrone.Common.Messaging;
+using NzbDrone.Core.IndexerSearch;
+using NzbDrone.Core.MediaFiles.Commands;
+
+namespace NzbDrone.Common.Test.MessagingTests
+{
+ [TestFixture]
+ public class CommandEqualityComparerFixture
+ {
+ [Test]
+ public void should_return_true_when_there_are_no_properties()
+ {
+ var command1 = new DownloadedEpisodesScanCommand();
+ var command2 = new DownloadedEpisodesScanCommand();
+ var comparer = new CommandEqualityComparer();
+
+ comparer.Equals(command1, command2).Should().BeTrue();
+ }
+
+ [Test]
+ public void should_return_true_when_single_property_matches()
+ {
+ var command1 = new EpisodeSearchCommand { EpisodeId = 1 };
+ var command2 = new EpisodeSearchCommand { EpisodeId = 1 };
+ var comparer = new CommandEqualityComparer();
+
+ comparer.Equals(command1, command2).Should().BeTrue();
+ }
+
+ [Test]
+ public void should_return_true_when_multiple_properties_match()
+ {
+ var command1 = new SeasonSearchCommand { SeriesId = 1, SeasonNumber = 1 };
+ var command2 = new SeasonSearchCommand { SeriesId = 1, SeasonNumber = 1 };
+ var comparer = new CommandEqualityComparer();
+
+ comparer.Equals(command1, command2).Should().BeTrue();
+ }
+
+ [Test]
+ public void should_return_false_when_single_property_doesnt_match()
+ {
+ var command1 = new EpisodeSearchCommand { EpisodeId = 1 };
+ var command2 = new EpisodeSearchCommand { EpisodeId = 2 };
+ var comparer = new CommandEqualityComparer();
+
+ comparer.Equals(command1, command2).Should().BeFalse();
+ }
+
+ [Test]
+ public void should_return_false_when_only_one_property_matches()
+ {
+ var command1 = new SeasonSearchCommand { SeriesId = 1, SeasonNumber = 1 };
+ var command2 = new SeasonSearchCommand { SeriesId = 1, SeasonNumber = 2 };
+ var comparer = new CommandEqualityComparer();
+
+ comparer.Equals(command1, command2).Should().BeFalse();
+ }
+
+ [Test]
+ public void should_return_false_when_no_properties_match()
+ {
+ var command1 = new SeasonSearchCommand { SeriesId = 1, SeasonNumber = 1 };
+ var command2 = new SeasonSearchCommand { SeriesId = 2, SeasonNumber = 2 };
+ var comparer = new CommandEqualityComparer();
+
+ comparer.Equals(command1, command2).Should().BeFalse();
+ }
+ }
+}
diff --git a/NzbDrone.Common.Test/NzbDrone.Common.Test.csproj b/NzbDrone.Common.Test/NzbDrone.Common.Test.csproj
index 4c22a7aeb..8fd34cbf6 100644
--- a/NzbDrone.Common.Test/NzbDrone.Common.Test.csproj
+++ b/NzbDrone.Common.Test/NzbDrone.Common.Test.csproj
@@ -69,6 +69,7 @@
+
diff --git a/NzbDrone.Common/Cache/CacheManger.cs b/NzbDrone.Common/Cache/CacheManger.cs
index d264eeb40..b5da3047c 100644
--- a/NzbDrone.Common/Cache/CacheManger.cs
+++ b/NzbDrone.Common/Cache/CacheManger.cs
@@ -28,7 +28,6 @@ namespace NzbDrone.Common.Cache
return GetCache(host, host.FullName);
}
-
public void Clear()
{
_cache.Clear();
diff --git a/NzbDrone.Common/Messaging/CommandEqualityComparer.cs b/NzbDrone.Common/Messaging/CommandEqualityComparer.cs
new file mode 100644
index 000000000..390319704
--- /dev/null
+++ b/NzbDrone.Common/Messaging/CommandEqualityComparer.cs
@@ -0,0 +1,44 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using NzbDrone.Common.EnvironmentInfo;
+
+namespace NzbDrone.Common.Messaging
+{
+ public class CommandEqualityComparer : IEqualityComparer
+ {
+ public bool Equals(ICommand x, ICommand y)
+ {
+ var xProperties = x.GetType().GetProperties();
+ var yProperties = y.GetType().GetProperties();
+
+ foreach (var xProperty in xProperties)
+ {
+ if (xProperty.Name == "CommandId")
+ {
+ continue;
+ }
+
+ var yProperty = yProperties.SingleOrDefault(p => p.Name == xProperty.Name);
+
+ if (yProperty == null)
+ {
+ continue;
+ }
+
+ if (!xProperty.GetValue(x, null).Equals(yProperty.GetValue(y, null)))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public int GetHashCode(ICommand obj)
+ {
+ return obj.CommandId.GetHashCode();
+ }
+ }
+}
diff --git a/NzbDrone.Common/Messaging/CommandCompletedEvent.cs b/NzbDrone.Common/Messaging/Events/CommandCompletedEvent.cs
similarity index 82%
rename from NzbDrone.Common/Messaging/CommandCompletedEvent.cs
rename to NzbDrone.Common/Messaging/Events/CommandCompletedEvent.cs
index 613800ae0..b65f25bf2 100644
--- a/NzbDrone.Common/Messaging/CommandCompletedEvent.cs
+++ b/NzbDrone.Common/Messaging/Events/CommandCompletedEvent.cs
@@ -1,4 +1,4 @@
-namespace NzbDrone.Common.Messaging
+namespace NzbDrone.Common.Messaging.Events
{
public class CommandCompletedEvent : IEvent
{
diff --git a/NzbDrone.Common/Messaging/CommandExecutedEvent.cs b/NzbDrone.Common/Messaging/Events/CommandExecutedEvent.cs
similarity index 82%
rename from NzbDrone.Common/Messaging/CommandExecutedEvent.cs
rename to NzbDrone.Common/Messaging/Events/CommandExecutedEvent.cs
index 3cb4e7f55..e5e9120b3 100644
--- a/NzbDrone.Common/Messaging/CommandExecutedEvent.cs
+++ b/NzbDrone.Common/Messaging/Events/CommandExecutedEvent.cs
@@ -1,4 +1,4 @@
-namespace NzbDrone.Common.Messaging
+namespace NzbDrone.Common.Messaging.Events
{
public class CommandExecutedEvent : IEvent
{
diff --git a/NzbDrone.Common/Messaging/CommandFailedEvent.cs b/NzbDrone.Common/Messaging/Events/CommandFailedEvent.cs
similarity index 88%
rename from NzbDrone.Common/Messaging/CommandFailedEvent.cs
rename to NzbDrone.Common/Messaging/Events/CommandFailedEvent.cs
index d33ab79f8..ef4934e41 100644
--- a/NzbDrone.Common/Messaging/CommandFailedEvent.cs
+++ b/NzbDrone.Common/Messaging/Events/CommandFailedEvent.cs
@@ -1,6 +1,6 @@
using System;
-namespace NzbDrone.Common.Messaging
+namespace NzbDrone.Common.Messaging.Events
{
public class CommandFailedEvent : IEvent
{
diff --git a/NzbDrone.Common/Messaging/CommandStartedEvent.cs b/NzbDrone.Common/Messaging/Events/CommandStartedEvent.cs
similarity index 82%
rename from NzbDrone.Common/Messaging/CommandStartedEvent.cs
rename to NzbDrone.Common/Messaging/Events/CommandStartedEvent.cs
index 8c1b4163d..762c9287c 100644
--- a/NzbDrone.Common/Messaging/CommandStartedEvent.cs
+++ b/NzbDrone.Common/Messaging/Events/CommandStartedEvent.cs
@@ -1,4 +1,4 @@
-namespace NzbDrone.Common.Messaging
+namespace NzbDrone.Common.Messaging.Events
{
public class CommandStartedEvent : IEvent
{
diff --git a/NzbDrone.Common/Messaging/ICommand.cs b/NzbDrone.Common/Messaging/ICommand.cs
index 005a84a68..e93f1ccaa 100644
--- a/NzbDrone.Common/Messaging/ICommand.cs
+++ b/NzbDrone.Common/Messaging/ICommand.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
namespace NzbDrone.Common.Messaging
{
diff --git a/NzbDrone.Common/Messaging/Manager/CommandManager.cs b/NzbDrone.Common/Messaging/Manager/CommandManager.cs
new file mode 100644
index 000000000..4666e9cae
--- /dev/null
+++ b/NzbDrone.Common/Messaging/Manager/CommandManager.cs
@@ -0,0 +1,59 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using NzbDrone.Common.Cache;
+using NzbDrone.Common.Messaging.Events;
+
+namespace NzbDrone.Common.Messaging.Manager
+{
+ public interface IManageCommands
+ {
+ ICollection Items { get; }
+ Boolean ExistingItem(ICommand command);
+ }
+
+ public class CommandManager : IManageCommands,
+ IHandle,
+ IHandle,
+ IHandle
+ {
+ private readonly ICached _cache;
+
+ public CommandManager(ICacheManger cacheManger)
+ {
+ _cache = cacheManger.GetCache(GetType());
+ }
+
+ public void Handle(CommandStartedEvent message)
+ {
+ _cache.Set(message.Command.CommandId, new CommandManagerItem(message.Command, CommandState.Running));
+ }
+
+ public void Handle(CommandCompletedEvent message)
+ {
+ _cache.Set(message.Command.CommandId, new CommandManagerItem(message.Command, CommandState.Completed));
+ }
+
+ public void Handle(CommandFailedEvent message)
+ {
+ _cache.Set(message.Command.CommandId, new CommandManagerItem(message.Command, CommandState.Failed));
+ }
+
+ public ICollection Items
+ {
+ get
+ {
+ return _cache.Values;
+ }
+ }
+
+ public bool ExistingItem(ICommand command)
+ {
+ var running = Items.Where(i => i.Type == command.GetType().FullName && i.State == CommandState.Running);
+
+ var result = running.Select(r => r.Command).Contains(command, new CommandEqualityComparer());
+
+ return result;
+ }
+ }
+}
diff --git a/NzbDrone.Common/Messaging/Manager/CommandManagerItem.cs b/NzbDrone.Common/Messaging/Manager/CommandManagerItem.cs
new file mode 100644
index 000000000..95c099151
--- /dev/null
+++ b/NzbDrone.Common/Messaging/Manager/CommandManagerItem.cs
@@ -0,0 +1,29 @@
+using System;
+
+namespace NzbDrone.Common.Messaging.Manager
+{
+ public class CommandManagerItem
+ {
+ public String Type { get; set; }
+ public ICommand Command { get; set; }
+ public CommandState State { get; set; }
+
+ public CommandManagerItem()
+ {
+ }
+
+ public CommandManagerItem(ICommand command, CommandState state)
+ {
+ Type = command.GetType().FullName;
+ Command = command;
+ State = state;
+ }
+ }
+
+ public enum CommandState
+ {
+ Running = 0,
+ Completed = 1,
+ Failed = 2
+ }
+}
diff --git a/NzbDrone.Common/Messaging/MessageAggregator.cs b/NzbDrone.Common/Messaging/MessageAggregator.cs
index 59c831b71..30f300a70 100644
--- a/NzbDrone.Common/Messaging/MessageAggregator.cs
+++ b/NzbDrone.Common/Messaging/MessageAggregator.cs
@@ -4,6 +4,8 @@ using System.Linq;
using System.Threading.Tasks;
using NLog;
using NzbDrone.Common.EnsureThat;
+using NzbDrone.Common.Messaging.Events;
+using NzbDrone.Common.Messaging.Manager;
using NzbDrone.Common.Serializer;
using NzbDrone.Common.TPL;
@@ -13,12 +15,14 @@ namespace NzbDrone.Common.Messaging
{
private readonly Logger _logger;
private readonly IServiceFactory _serviceFactory;
+ private readonly IManageCommands _commandManager;
private readonly TaskFactory _taskFactory;
- public MessageAggregator(Logger logger, IServiceFactory serviceFactory)
+ public MessageAggregator(Logger logger, IServiceFactory serviceFactory, IManageCommands commandManager)
{
_logger = logger;
_serviceFactory = serviceFactory;
+ _commandManager = commandManager;
var scheduler = new LimitedConcurrencyLevelTaskScheduler(2);
_taskFactory = new TaskFactory(scheduler);
}
@@ -86,6 +90,12 @@ namespace NzbDrone.Common.Messaging
try
{
+ if (_commandManager.ExistingItem(command))
+ {
+ _logger.Info("Command is already in progress: {0}", command.GetType().Name);
+ return;
+ }
+
PublishEvent(new CommandStartedEvent(command));
handler.Execute(command);
sw.Stop();
diff --git a/NzbDrone.Common/NzbDrone.Common.csproj b/NzbDrone.Common/NzbDrone.Common.csproj
index c1b17236f..105c14d22 100644
--- a/NzbDrone.Common/NzbDrone.Common.csproj
+++ b/NzbDrone.Common/NzbDrone.Common.csproj
@@ -92,6 +92,10 @@
+
+
+
+
@@ -105,9 +109,9 @@
-
-
-
+
+
+
diff --git a/NzbDrone.Core/Jobs/TaskManager.cs b/NzbDrone.Core/Jobs/TaskManager.cs
index 4c82da90a..83b079769 100644
--- a/NzbDrone.Core/Jobs/TaskManager.cs
+++ b/NzbDrone.Core/Jobs/TaskManager.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using NLog;
using NzbDrone.Common.Messaging;
+using NzbDrone.Common.Messaging.Events;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Configuration.Events;
using NzbDrone.Core.Indexers;
diff --git a/NzbDrone.Core/Providers/XemProvider.cs b/NzbDrone.Core/Providers/XemProvider.cs
index 931793dfb..f9d86f85c 100644
--- a/NzbDrone.Core/Providers/XemProvider.cs
+++ b/NzbDrone.Core/Providers/XemProvider.cs
@@ -131,8 +131,7 @@ namespace NzbDrone.Core.Providers
catch (Exception ex)
{
- //TODO: We should increase this back to warn when caching is in place
- _logger.TraceException("Error updating scene numbering mappings for: " + series, ex);
+ _logger.ErrorException("Error updating scene numbering mappings for: " + series, ex);
}
}