More nzbdrone.exe refactoring.

pull/7/merge
kay.one 13 years ago
parent 69ba365cd3
commit 8bf4f81a04

@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using AutoMoq;
using FizzWare.NBuilder;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Providers;
namespace NzbDrone.App.Test
{
[TestFixture]
public class MonitoringProviderTest
{
[Test]
public void Ensure_priority_doesnt_fail_on_invalid_iis_proccess_id()
{
var mocker = new AutoMoqer();
var processMock = mocker.GetMock<ProcessProvider>();
processMock.Setup(c => c.GetCurrentProcess())
.Returns(Builder<ProcessInfo>.CreateNew().With(c => c.Priority == ProcessPriorityClass.Normal).Build());
processMock.Setup(c => c.GetProcessById(It.IsAny<int>())).Returns((ProcessInfo)null);
var subject = mocker.Resolve<MonitoringProvider>();
//Act
subject.EnsurePriority(null, null);
}
}
}

@ -0,0 +1,166 @@
// ReSharper disable RedundantUsingDirective
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Runtime.CompilerServices;
using AutoMoq.Unity;
using Microsoft.Practices.Unity;
using Moq;
using Moq.Language.Flow;
[assembly: InternalsVisibleTo("AutoMoq.Tests")]
namespace AutoMoq
{
public class AutoMoqer
{
internal readonly MockBehavior DefaultBehavior = MockBehavior.Default;
internal Type ResolveType;
private IUnityContainer container;
private IDictionary<Type, object> registeredMocks;
public AutoMoqer()
{
SetupAutoMoqer(new UnityContainer());
}
public AutoMoqer(MockBehavior defaultBehavior)
{
DefaultBehavior = defaultBehavior;
SetupAutoMoqer(new UnityContainer());
}
internal AutoMoqer(IUnityContainer container)
{
SetupAutoMoqer(container);
}
public virtual T Resolve<T>()
{
ResolveType = typeof(T);
var result = container.Resolve<T>();
SetConstant(result);
ResolveType = null;
return result;
}
public virtual Mock<T> GetMock<T>() where T : class
{
return GetMock<T>(DefaultBehavior);
}
public virtual Mock<T> GetMock<T>(MockBehavior behavior) where T : class
{
ResolveType = null;
var type = GetTheMockType<T>();
if (GetMockHasNotBeenCalledForThisType(type))
{
CreateANewMockAndRegisterIt<T>(type, behavior);
}
var mock = TheRegisteredMockForThisType<T>(type);
if (behavior != MockBehavior.Default && mock.Behavior == MockBehavior.Default)
{
throw new InvalidOperationException("Unable to change be behaviour of a an existing mock.");
}
return mock;
}
internal virtual void SetMock(Type type, Mock mock)
{
if (registeredMocks.ContainsKey(type) == false)
registeredMocks.Add(type, mock);
}
public virtual void SetConstant<T>(T instance)
{
container.RegisterInstance(instance);
SetMock(instance.GetType(), null);
}
public ISetup<T> Setup<T>(Expression<Action<T>> expression) where T : class
{
return GetMock<T>().Setup(expression);
}
public ISetup<T, TResult> Setup<T, TResult>(Expression<Func<T, TResult>> expression) where T : class
{
return GetMock<T>().Setup(expression);
}
public void Verify<T>(Expression<Action<T>> expression) where T : class
{
GetMock<T>().Verify(expression);
}
public void Verify<T>(Expression<Action<T>> expression, string failMessage) where T : class
{
GetMock<T>().Verify(expression, failMessage);
}
public void Verify<T>(Expression<Action<T>> expression, Times times) where T : class
{
GetMock<T>().Verify(expression, times);
}
public void Verify<T>(Expression<Action<T>> expression, Times times, string failMessage) where T : class
{
GetMock<T>().Verify(expression, times, failMessage);
}
public void VerifyAllMocks()
{
foreach (var registeredMock in registeredMocks)
{
var mock = registeredMock.Value as Mock;
if (mock != null)
mock.VerifyAll();
}
}
#region private methods
private void SetupAutoMoqer(IUnityContainer container)
{
this.container = container;
container.RegisterInstance(this);
registeredMocks = new Dictionary<Type, object>();
AddTheAutoMockingContainerExtensionToTheContainer(container);
}
private static void AddTheAutoMockingContainerExtensionToTheContainer(IUnityContainer container)
{
container.AddNewExtension<AutoMockingContainerExtension>();
return;
}
private Mock<T> TheRegisteredMockForThisType<T>(Type type) where T : class
{
return (Mock<T>)registeredMocks.Where(x => x.Key == type).First().Value;
}
private void CreateANewMockAndRegisterIt<T>(Type type, MockBehavior behavior) where T : class
{
var mock = new Mock<T>(behavior);
container.RegisterInstance(mock.Object);
SetMock(type, mock);
}
private bool GetMockHasNotBeenCalledForThisType(Type type)
{
return registeredMocks.ContainsKey(type) == false;
}
private static Type GetTheMockType<T>() where T : class
{
return typeof(T);
}
#endregion
}
}

@ -0,0 +1,187 @@
// ReSharper disable RedundantUsingDirective
using System;
using AutoMoq;
using Moq;
using NUnit.Framework;
namespace NzbDrone.Core.Test
{
[TestFixture]
// ReSharper disable InconsistentNaming
public class AutoMoqerTest
{
[Test]
public void GetMock_on_interface_returns_mock()
{
//Arrange
var mocker = new AutoMoqer();
//Act
var mock = mocker.GetMock<IDependency>();
//Assert
Assert.IsNotNull(mock);
}
[Test]
public void GetMock_on_concrete_returns_mock()
{
//Arrange
var mocker = new AutoMoqer();
//Act
var mock = mocker.GetMock<ConcreteClass>();
//Assert
Assert.IsNotNull(mock);
}
[Test]
public void Resolve_doesnt_return_mock()
{
//Arrange
var mocker = new AutoMoqer();
//Act
var result = mocker.Resolve<ConcreteClass>().Do();
//Assert
Assert.AreEqual("hello", result);
}
[Test]
public void Resolve_with_dependency_doesnt_return_mock()
{
//Arrange
var mocker = new AutoMoqer();
//Act
var result = mocker.Resolve<VirtualDependency>().VirtualMethod();
//Assert
Assert.AreEqual("hello", result);
}
[Test]
public void Resolve_with_mocked_dependency_uses_mock()
{
//Arrange
var mocker = new AutoMoqer();
mocker.GetMock<VirtualDependency>()
.Setup(m => m.VirtualMethod())
.Returns("mocked");
//Act
var result = mocker.Resolve<ClassWithVirtualDependencies>().CallVirtualChild();
//Assert
Assert.AreEqual("mocked", result);
}
[Test]
public void Resolve_with_unbound_concerete_dependency_uses_mock()
{
//Arrange
var mocker = new AutoMoqer();
//Act
var result = mocker.Resolve<ClassWithVirtualDependencies>().CallVirtualChild();
var mockedResult = new Mock<VirtualDependency>().Object.VirtualMethod();
//Assert
Assert.AreEqual(mockedResult, result);
}
[Test]
public void Resolve_with_constant_concerete_dependency_uses_constant()
{
//Arrange
var mocker = new AutoMoqer();
var constant = new VirtualDependency { PropValue = Guid.NewGuid().ToString() };
mocker.SetConstant(constant);
//Act
var result = mocker.Resolve<ClassWithVirtualDependencies>().GetVirtualProperty();
//Assert
Assert.AreEqual(constant.PropValue, result);
}
}
public class ConcreteClass
{
public string Do()
{
return "hello";
}
}
public class Dependency : IDependency
{
}
public interface IDependency
{
}
public class ClassWithDependencies
{
public ClassWithDependencies(IDependency dependency)
{
Dependency = dependency;
}
public IDependency Dependency { get; set; }
}
public class ClassWithVirtualDependencies
{
private readonly VirtualDependency _virtualDependency;
public ClassWithVirtualDependencies(IDependency dependency, VirtualDependency virtualDependency)
{
_virtualDependency = virtualDependency;
Dependency = dependency;
}
public IDependency Dependency { get; set; }
public string CallVirtualChild()
{
return _virtualDependency.VirtualMethod();
}
public string GetVirtualProperty()
{
return _virtualDependency.PropValue;
}
}
public class VirtualDependency
{
private readonly IDependency _dependency;
public VirtualDependency()
{
}
public VirtualDependency(IDependency dependency)
{
_dependency = dependency;
}
public string PropValue { get; set; }
public virtual string VirtualMethod()
{
return "hello";
}
}
}

@ -0,0 +1,22 @@
 Copyright (c) 2010 Darren Cauthon
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

@ -0,0 +1,84 @@
// ReSharper disable RedundantUsingDirective
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Microsoft.Practices.ObjectBuilder2;
using Microsoft.Practices.Unity;
using Moq;
namespace AutoMoq.Unity
{
internal class AutoMockingBuilderStrategy : BuilderStrategy
{
private readonly IUnityContainer _container;
private readonly MockRepository _mockFactory;
private readonly IEnumerable<Type> _registeredTypes;
public AutoMockingBuilderStrategy(IEnumerable<Type> registeredTypes, IUnityContainer container)
{
var autoMoqer = container.Resolve<AutoMoqer>();
_mockFactory = new MockRepository(autoMoqer.DefaultBehavior);
_registeredTypes = registeredTypes;
_container = container;
}
public override void PreBuildUp(IBuilderContext context)
{
var autoMoqer = _container.Resolve<AutoMoqer>();
var type = GetTheTypeFromTheBuilderContext(context);
if (AMockObjectShouldBeCreatedForThisType(type))
{
var mock = CreateAMockObject(type);
context.Existing = mock.Object;
autoMoqer.SetMock(type, mock);
}
}
#region private methods
private bool AMockObjectShouldBeCreatedForThisType(Type type)
{
var mocker = _container.Resolve<AutoMoqer>();
return TypeIsNotRegistered(type) && (mocker.ResolveType == null || mocker.ResolveType != type);
//return TypeIsNotRegistered(type) && type.IsInterface;
}
private static Type GetTheTypeFromTheBuilderContext(IBuilderContext context)
{
return (context.OriginalBuildKey).Type;
}
private bool TypeIsNotRegistered(Type type)
{
return _registeredTypes.Any(x => x.Equals(type)) == false;
}
private Mock CreateAMockObject(Type type)
{
var createMethod = GenerateAnInterfaceMockCreationMethod(type);
return InvokeTheMockCreationMethod(createMethod);
}
private Mock InvokeTheMockCreationMethod(MethodInfo createMethod)
{
return (Mock)createMethod.Invoke(_mockFactory, new object[] { new List<object>().ToArray() });
}
private MethodInfo GenerateAnInterfaceMockCreationMethod(Type type)
{
var createMethodWithNoParameters = _mockFactory.GetType().GetMethod("Create", EmptyArgumentList());
return createMethodWithNoParameters.MakeGenericMethod(new[] { type });
}
private static Type[] EmptyArgumentList()
{
return new[] { typeof(object[]) };
}
#endregion
}
}

@ -0,0 +1,40 @@
// ReSharper disable RedundantUsingDirective
using System;
using System.Collections.Generic;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.ObjectBuilder;
namespace AutoMoq.Unity
{
internal class AutoMockingContainerExtension : UnityContainerExtension
{
private readonly IList<Type> registeredTypes = new List<Type>();
protected override void Initialize()
{
SetEventsOnContainerToTrackAllRegisteredTypes();
SetBuildingStrategyForBuildingUnregisteredTypes();
}
#region private methods
private void SetEventsOnContainerToTrackAllRegisteredTypes()
{
Context.Registering += ((sender, e) => RegisterType(e.TypeFrom));
Context.RegisteringInstance += ((sender, e) => RegisterType(e.RegisteredType));
}
private void RegisterType(Type typeToRegister)
{
registeredTypes.Add(typeToRegister);
}
private void SetBuildingStrategyForBuildingUnregisteredTypes()
{
var strategy = new AutoMockingBuilderStrategy(registeredTypes, Container);
Context.Strategies.Add(strategy, UnityBuildStage.PreCreation);
}
#endregion
}
}

@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using AutoMoq;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Providers;
namespace NzbDrone.App.Test
{
[TestFixture]
public class IISProviderTest
{
[Test]
public void start_should_set_IISProccessId_property()
{
var mocker = new AutoMoqer();
var configMock = mocker.GetMock<ConfigProvider>();
configMock.SetupGet(c => c.IISExePath).Returns("NzbDrone.Test.Dummy.exe");
mocker.Resolve<ProcessProvider>();
var iisProvider = mocker.Resolve<IISProvider>();
iisProvider.StartServer();
iisProvider.IISProcessId.Should().NotBe(0);
}
}
}

@ -31,9 +31,21 @@
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="FizzWare.NBuilder">
<HintPath>..\packages\NBuilder.3.0.1\lib\FizzWare.NBuilder.dll</HintPath>
</Reference>
<Reference Include="FluentAssertions"> <Reference Include="FluentAssertions">
<HintPath>..\packages\FluentAssertions.1.5.0.0\Lib\.NetFramework 4.0\FluentAssertions.dll</HintPath> <HintPath>..\packages\FluentAssertions.1.5.0.0\Lib\.NetFramework 4.0\FluentAssertions.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.Practices.ServiceLocation">
<HintPath>..\packages\CommonServiceLocator.1.0\lib\NET35\Microsoft.Practices.ServiceLocation.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Practices.Unity">
<HintPath>..\packages\Unity.2.1.505.0\lib\NET35\Microsoft.Practices.Unity.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Practices.Unity.Configuration">
<HintPath>..\packages\Unity.2.1.505.0\lib\NET35\Microsoft.Practices.Unity.Configuration.dll</HintPath>
</Reference>
<Reference Include="Moq"> <Reference Include="Moq">
<HintPath>..\packages\Moq.4.0.10827\lib\NET40\Moq.dll</HintPath> <HintPath>..\packages\Moq.4.0.10827\lib\NET40\Moq.dll</HintPath>
</Reference> </Reference>
@ -56,6 +68,13 @@
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="AutoMoq\AutoMoqer.cs" />
<Compile Include="AutoMoq\AutoMoqerTest.cs" />
<Compile Include="AutoMoq\Unity\AutoMockingBuilderStrategy.cs" />
<Compile Include="AutoMoq\Unity\AutoMockingContainerExtension.cs" />
<Compile Include="ApplicationTest.cs" />
<Compile Include="IISProviderTest.cs" />
<Compile Include="ProcessProviderTests.cs" />
<Compile Include="EnviromentControllerTest.cs" /> <Compile Include="EnviromentControllerTest.cs" />
<Compile Include="ServiceControllerTests.cs" /> <Compile Include="ServiceControllerTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
@ -64,11 +83,18 @@
<None Include="packages.config" /> <None Include="packages.config" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\NzbDrone.Test.Dummy\NzbDrone.Test.Dummy.csproj">
<Project>{FAFB5948-A222-4CF6-AD14-026BE7564802}</Project>
<Name>NzbDrone.Test.Dummy</Name>
</ProjectReference>
<ProjectReference Include="..\NzbDrone\NzbDrone.csproj"> <ProjectReference Include="..\NzbDrone\NzbDrone.csproj">
<Project>{D12F7F2F-8A3C-415F-88FA-6DD061A84869}</Project> <Project>{D12F7F2F-8A3C-415F-88FA-6DD061A84869}</Project>
<Name>NzbDrone</Name> <Name>NzbDrone</Name>
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup>
<Content Include="AutoMoq\License.txt" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. Other similar extension points exist, see Microsoft.Common.targets.

@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Providers;
namespace NzbDrone.App.Test
{
[TestFixture]
public class ProcessProviderTests
{
ProcessProvider _processProvider;
[SetUp]
public void Setup()
{
_processProvider = new ProcessProvider();
}
[TestCase(0)]
[TestCase(123332324)]
public void Kill_should_not_fail_on_invalid_process_is(int processId)
{
_processProvider.Kill(processId);
}
[Test]
public void GetById_should_return_null_if_process_doesnt_exist()
{
_processProvider.GetProcessById(1234567).Should().BeNull();
}
[Test]
public void Should_be_able_to_kill_procces()
{
var dummyProcess = StartDummyProcess();
_processProvider.Kill(dummyProcess.Id);
dummyProcess.HasExited.Should().BeTrue();
}
public Process StartDummyProcess()
{
return Process.Start("NzbDrone.Test.Dummy.exe");
}
}
}

@ -1,6 +1,9 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="CommonServiceLocator" version="1.0" />
<package id="FluentAssertions" version="1.5.0.0" /> <package id="FluentAssertions" version="1.5.0.0" />
<package id="Moq" version="4.0.10827" /> <package id="Moq" version="4.0.10827" />
<package id="NBuilder" version="3.0.1" />
<package id="NUnit" version="2.5.10.11092" /> <package id="NUnit" version="2.5.10.11092" />
<package id="Unity" version="2.1.505.0" />
</packages> </packages>

@ -0,0 +1,57 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{FAFB5948-A222-4CF6-AD14-026BE7564802}</ProjectGuid>
<OutputType>Exe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>NzbDrone.Test.Dummy</RootNamespace>
<AssemblyName>NzbDrone.Test.Dummy</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<TargetFrameworkProfile>Client</TargetFrameworkProfile>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<PlatformTarget>x86</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<PlatformTarget>x86</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
namespace NzbDrone.Test.Dummy
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Dummy process. ID:{0} Path:{1}", Process.GetCurrentProcess().Id, Process.GetCurrentProcess().MainModule.FileName);
Console.ReadLine();
}
}
}

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("NzbDrone.Test.Dummy")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Microsoft")]
[assembly: AssemblyProduct("NzbDrone.Test.Dummy")]
[assembly: AssemblyCopyright("Copyright © Microsoft 2011")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("7b773a86-574d-48c3-9e89-6f2e0dff714b")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

@ -13,6 +13,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{57A04B72
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NzbDrone.App.Test", "NzbDrone.App.Test\NzbDrone.App.Test.csproj", "{C0EA1A40-91AD-4EEB-BD16-2DDDEBD20AE5}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NzbDrone.App.Test", "NzbDrone.App.Test\NzbDrone.App.Test.csproj", "{C0EA1A40-91AD-4EEB-BD16-2DDDEBD20AE5}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NzbDrone.Test.Dummy", "NzbDrone.Test.Dummy\NzbDrone.Test.Dummy.csproj", "{FAFB5948-A222-4CF6-AD14-026BE7564802}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -91,6 +93,18 @@ Global
{C0EA1A40-91AD-4EEB-BD16-2DDDEBD20AE5}.Release|Mixed Platforms.Build.0 = Release|Any CPU {C0EA1A40-91AD-4EEB-BD16-2DDDEBD20AE5}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{C0EA1A40-91AD-4EEB-BD16-2DDDEBD20AE5}.Release|x64.ActiveCfg = Release|Any CPU {C0EA1A40-91AD-4EEB-BD16-2DDDEBD20AE5}.Release|x64.ActiveCfg = Release|Any CPU
{C0EA1A40-91AD-4EEB-BD16-2DDDEBD20AE5}.Release|x86.ActiveCfg = Release|Any CPU {C0EA1A40-91AD-4EEB-BD16-2DDDEBD20AE5}.Release|x86.ActiveCfg = Release|Any CPU
{FAFB5948-A222-4CF6-AD14-026BE7564802}.Debug|Any CPU.ActiveCfg = Debug|x86
{FAFB5948-A222-4CF6-AD14-026BE7564802}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
{FAFB5948-A222-4CF6-AD14-026BE7564802}.Debug|Mixed Platforms.Build.0 = Debug|x86
{FAFB5948-A222-4CF6-AD14-026BE7564802}.Debug|x64.ActiveCfg = Debug|x86
{FAFB5948-A222-4CF6-AD14-026BE7564802}.Debug|x86.ActiveCfg = Debug|x86
{FAFB5948-A222-4CF6-AD14-026BE7564802}.Debug|x86.Build.0 = Debug|x86
{FAFB5948-A222-4CF6-AD14-026BE7564802}.Release|Any CPU.ActiveCfg = Release|x86
{FAFB5948-A222-4CF6-AD14-026BE7564802}.Release|Mixed Platforms.ActiveCfg = Release|x86
{FAFB5948-A222-4CF6-AD14-026BE7564802}.Release|Mixed Platforms.Build.0 = Release|x86
{FAFB5948-A222-4CF6-AD14-026BE7564802}.Release|x64.ActiveCfg = Release|x86
{FAFB5948-A222-4CF6-AD14-026BE7564802}.Release|x86.ActiveCfg = Release|x86
{FAFB5948-A222-4CF6-AD14-026BE7564802}.Release|x86.Build.0 = Release|x86
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@ -98,6 +112,7 @@ Global
GlobalSection(NestedProjects) = preSolution GlobalSection(NestedProjects) = preSolution
{193ADD3B-792B-4173-8E4C-5A3F8F0237F0} = {57A04B72-8088-4F75-A582-1158CF8291F7} {193ADD3B-792B-4173-8E4C-5A3F8F0237F0} = {57A04B72-8088-4F75-A582-1158CF8291F7}
{C0EA1A40-91AD-4EEB-BD16-2DDDEBD20AE5} = {57A04B72-8088-4F75-A582-1158CF8291F7} {C0EA1A40-91AD-4EEB-BD16-2DDDEBD20AE5} = {57A04B72-8088-4F75-A582-1158CF8291F7}
{FAFB5948-A222-4CF6-AD14-026BE7564802} = {57A04B72-8088-4F75-A582-1158CF8291F7}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
EnterpriseLibraryConfigurationToolBinariesPath = packages\Unity.2.1.505.0\lib\NET35 EnterpriseLibraryConfigurationToolBinariesPath = packages\Unity.2.1.505.0\lib\NET35

@ -1,14 +1,12 @@
using System; using System;
using System.Diagnostics;
using System.Net; using System.Net;
using System.Threading; using System.Threading;
using System.Timers;
using NLog; using NLog;
using NzbDrone.Providers; using NzbDrone.Providers;
namespace NzbDrone namespace NzbDrone
{ {
internal class Application public class Application
{ {
private static readonly Logger Logger = LogManager.GetLogger("Application"); private static readonly Logger Logger = LogManager.GetLogger("Application");
@ -36,22 +34,14 @@ namespace NzbDrone
Logger.Info("Starting NZBDrone. Start-up Path:'{0}'", _configProvider.ApplicationRoot); Logger.Info("Starting NZBDrone. Start-up Path:'{0}'", _configProvider.ApplicationRoot);
Thread.CurrentThread.Name = "Host"; Thread.CurrentThread.Name = "Host";
AppDomain.CurrentDomain.UnhandledException += ((s, e) => AppDomainException(e));
AppDomain.CurrentDomain.ProcessExit += ProgramExited;
AppDomain.CurrentDomain.DomainUnload += ProgramExited;
} }
internal void Start() public void Start()
{ {
_iisProvider.StopServer(); _iisProvider.StopServer();
_iisProvider.StartServer(); _iisProvider.StartServer();
_debuggerProvider.Attach();
var prioCheckTimer = new System.Timers.Timer(5000); _debuggerProvider.Attach();
prioCheckTimer.Elapsed += EnsurePriority;
prioCheckTimer.Enabled = true;
if (_enviromentProvider.IsUserInteractive && _configProvider.LaunchBrowser) if (_enviromentProvider.IsUserInteractive && _configProvider.LaunchBrowser)
{ {
@ -79,51 +69,10 @@ namespace NzbDrone
} }
} }
internal void Stop() public void Stop()
{
}
private void AppDomainException(object excepion)
{
Console.WriteLine("EPIC FAIL: {0}", excepion);
Logger.Fatal("EPIC FAIL: {0}", excepion);
#if RELEASE
new Client
{
ApiKey = "43BBF60A-EB2A-4C1C-B09E-422ADF637265",
ApplicationName = "NZBDrone",
CurrentException = excepion as Exception
}.Submit();
#endif
}
internal void EnsurePriority(object sender, ElapsedEventArgs e)
{ {
var currentProcessId = _processProvider.GetCurrentProcessId();
if (_processProvider.GetProcessPriority(currentProcessId) != ProcessPriorityClass.Normal)
{
_processProvider.SetPriority(_processProvider.GetCurrentProcessId(), ProcessPriorityClass.Normal);
}
var iisProcessPriority = _processProvider.GetProcessPriority(_iisProvider.IISProcessId);
if (iisProcessPriority != ProcessPriorityClass.Normal && iisProcessPriority != ProcessPriorityClass.AboveNormal)
{
_processProvider.SetPriority(_iisProvider.IISProcessId, ProcessPriorityClass.Normal);
}
} }
private void ProgramExited(object sender, EventArgs e)
{
_iisProvider.StopServer();
}
} }
} }

@ -86,6 +86,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Application.cs" /> <Compile Include="Application.cs" />
<Compile Include="ProcessInfo.cs" />
<Compile Include="Providers\ConsoleProvider.cs" /> <Compile Include="Providers\ConsoleProvider.cs" />
<Compile Include="Providers\DebuggerProvider.cs" /> <Compile Include="Providers\DebuggerProvider.cs" />
<Compile Include="Providers\EnviromentProvider.cs" /> <Compile Include="Providers\EnviromentProvider.cs" />
@ -97,6 +98,7 @@
<Compile Include="Providers\IISProvider.cs" /> <Compile Include="Providers\IISProvider.cs" />
<Compile Include="Program.cs" /> <Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Providers\MonitoringProvider.cs" />
<Compile Include="Providers\ProcessProvider.cs" /> <Compile Include="Providers\ProcessProvider.cs" />
<Compile Include="Providers\ServiceProvider.cs" /> <Compile Include="Providers\ServiceProvider.cs" />
<Compile Include="Providers\WebClientProvider.cs" /> <Compile Include="Providers\WebClientProvider.cs" />

@ -10,17 +10,12 @@ using System.Collections.Generic;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using EnvDTE; using EnvDTE;
using EnvDTE80; using EnvDTE80;
using NLog;
using Thread = System.Threading.Thread; using Thread = System.Threading.Thread;
namespace NzbDrone namespace NzbDrone
{ {
public class ProcessAttacher public class ProcessAttacher
{ {
private static readonly Logger Logger = LogManager.GetLogger("Application");
public static void Attach() public static void Attach()
{ {
DTE2 dte2; DTE2 dte2;

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
namespace NzbDrone
{
public class ProcessInfo
{
public int Id { get; set; }
public ProcessPriorityClass Priority { get; set; }
public string StartPath { get; set; }
public bool HasExited { get; set; }
}
}

@ -1,10 +1,11 @@
using System; using System;
using NLog; using NLog;
using Ninject; using Ninject;
using NzbDrone.Providers;
namespace NzbDrone namespace NzbDrone
{ {
internal static class Program public static class Program
{ {
public static readonly StandardKernel Kernel = new StandardKernel(); public static readonly StandardKernel Kernel = new StandardKernel();
@ -14,8 +15,20 @@ namespace NzbDrone
{ {
try try
{ {
Kernel.Bind<ConfigProvider>().ToSelf().InSingletonScope();
Kernel.Bind<ConsoleProvider>().ToSelf().InSingletonScope();
Kernel.Bind<DebuggerProvider>().ToSelf().InSingletonScope();
Kernel.Bind<EnviromentProvider>().ToSelf().InSingletonScope();
Kernel.Bind<IISProvider>().ToSelf().InSingletonScope();
Kernel.Bind<MonitoringProvider>().ToSelf().InSingletonScope();
Kernel.Bind<ProcessProvider>().ToSelf().InSingletonScope();
Kernel.Bind<ServiceProvider>().ToSelf().InSingletonScope();
Kernel.Bind<WebClientProvider>().ToSelf().InSingletonScope();
Console.WriteLine("Starting Console."); Console.WriteLine("Starting Console.");
Kernel.Get<MonitoringProvider>().Start();
Kernel.Get<Application>().Start(); Kernel.Get<Application>().Start();
} }
catch (Exception e) catch (Exception e)
{ {

@ -36,4 +36,4 @@ using System.Runtime.InteropServices;
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("0.5.0.*")] [assembly: AssemblyVersion("0.5.0.*")]

@ -3,15 +3,17 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Xml.Linq; using System.Xml.Linq;
using System.Xml.XPath;
using NLog; using NLog;
using NLog.Config; using NLog.Config;
namespace NzbDrone.Providers namespace NzbDrone.Providers
{ {
internal class ConfigProvider public class ConfigProvider
{ {
private static readonly Logger Logger = LogManager.GetLogger("ConfigProvider");
internal virtual string ApplicationRoot public virtual string ApplicationRoot
{ {
get get
{ {
@ -27,38 +29,74 @@ namespace NzbDrone.Providers
} }
} }
internal virtual int Port public virtual int Port
{ {
get { return GetValueInt("Port"); } get { return GetValueInt("Port"); }
} }
internal virtual bool LaunchBrowser public virtual bool LaunchBrowser
{ {
get { return GetValueBoolean("LaunchBrowser"); } get { return GetValueBoolean("LaunchBrowser"); }
} }
internal virtual string AppDataDirectory public virtual string AppDataDirectory
{ {
get { return Path.Combine(ApplicationRoot, "NzbDrone.Web", "App_Data"); } get { return Path.Combine(ApplicationRoot, "NzbDrone.Web", "App_Data"); }
} }
internal virtual string ConfigFile public virtual string ConfigFile
{ {
get { return Path.Combine(AppDataDirectory, "Config.xml"); } get { return Path.Combine(AppDataDirectory, "Config.xml"); }
} }
internal virtual string IISFolder public virtual string IISFolder
{ {
get { return Path.Combine(ApplicationRoot, @"IISExpress\"); } get { return Path.Combine(ApplicationRoot, @"IISExpress\"); }
} }
internal virtual void ConfigureNlog() public virtual string IISExePath
{
get { return IISFolder + @"iisexpress.exe"; }
}
public virtual string IISConfigPath
{
get { return Path.Combine(IISFolder, "AppServer", "applicationhost.config"); }
}
public virtual void ConfigureNlog()
{ {
LogManager.Configuration = new XmlLoggingConfiguration( LogManager.Configuration = new XmlLoggingConfiguration(
Path.Combine(ApplicationRoot, "NzbDrone.Web\\log.config"), false); Path.Combine(ApplicationRoot, "NzbDrone.Web\\log.config"), false);
} }
internal virtual void CreateDefaultConfigFile() public virtual void UpdateIISConfig(string configPath)
{
Logger.Info(@"Server configuration file: {0}", configPath);
Logger.Info(@"Configuring server to: [http://localhost:{0}]", Port);
var configXml = XDocument.Load(configPath);
var bindings =
configXml.XPathSelectElement("configuration/system.applicationHost/sites").Elements("site").Where(
d => d.Attribute("name").Value.ToLowerInvariant() == "nzbdrone").First().Element("bindings");
bindings.Descendants().Remove();
bindings.Add(
new XElement("binding",
new XAttribute("protocol", "http"),
new XAttribute("bindingInformation", String.Format("*:{0}:localhost", Port))
));
bindings.Add(
new XElement("binding",
new XAttribute("protocol", "http"),
new XAttribute("bindingInformation", String.Format("*:{0}:", Port))
));
configXml.Save(configPath);
}
public virtual void CreateDefaultConfigFile()
{ {
//Create the config file here //Create the config file here
Directory.CreateDirectory(AppDataDirectory); Directory.CreateDirectory(AppDataDirectory);
@ -69,7 +107,7 @@ namespace NzbDrone.Providers
} }
} }
internal virtual void WriteDefaultConfig() public virtual void WriteDefaultConfig()
{ {
var xDoc = new XDocument(new XDeclaration("1.0", "utf-8", "yes")); var xDoc = new XDocument(new XDeclaration("1.0", "utf-8", "yes"));

@ -1,7 +1,4 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace NzbDrone.Providers namespace NzbDrone.Providers
{ {

@ -1,20 +1,17 @@
using System; using System;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading; using System.Threading;
using NLog; using NLog;
namespace NzbDrone.Providers namespace NzbDrone.Providers
{ {
internal class DebuggerProvider public class DebuggerProvider
{ {
private static readonly Logger Logger = LogManager.GetLogger("DebuggerProvider"); private static readonly Logger Logger = LogManager.GetLogger("DebuggerProvider");
internal virtual void Attach() public virtual void Attach()
{ {
#if DEBUG #if DEBUG
if (Debugger.IsAttached) if (Debugger.IsAttached)

@ -1,104 +1,79 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq;
using System.Net;
using System.Runtime.Remoting;
using System.Timers;
using System.Xml.Linq;
using System.Xml.XPath;
using NLog; using NLog;
using Ninject;
namespace NzbDrone.Providers namespace NzbDrone.Providers
{ {
internal class IISProvider public class IISProvider
{ {
private readonly ConfigProvider _configProvider; private readonly ConfigProvider _configProvider;
private readonly ProcessProvider _processProvider;
private static readonly Logger IISLogger = LogManager.GetLogger("IISExpress"); private static readonly Logger IISLogger = LogManager.GetLogger("IISExpress");
private static readonly Logger Logger = LogManager.GetLogger("IISProvider"); private static readonly Logger Logger = LogManager.GetLogger("IISProvider");
private readonly string IISExe;
private readonly string IISConfigPath;
private static Timer _pingTimer; [Inject]
private static int _pingFailCounter; public IISProvider(ConfigProvider configProvider, ProcessProvider processProvider)
{
private static Process _iisProcess; _configProvider = configProvider;
_processProvider = processProvider;
}
public IISProvider(ConfigProvider configProvider) public IISProvider()
{ {
_configProvider = configProvider;
IISExe = Path.Combine(_configProvider.IISFolder, @"iisexpress.exe");
IISConfigPath = Path.Combine(_configProvider.IISFolder, "AppServer", "applicationhost.config");
} }
internal string AppUrl public string AppUrl
{ {
get { return string.Format("http://localhost:{0}/", _configProvider.Port); } get { return string.Format("http://localhost:{0}/", _configProvider.Port); }
} }
internal int IISProcessId public int IISProcessId { get; private set; }
{
get
{
if (_iisProcess == null)
{
throw new InvalidOperationException("IIS Process isn't running yet.");
}
return _iisProcess.Id; public bool ServerStarted { get; private set; }
}
}
internal Process StartServer() public void StartServer()
{ {
Logger.Info("Preparing IISExpress Server..."); Logger.Info("Preparing IISExpress Server...");
_iisProcess = new Process();
_iisProcess.StartInfo.FileName = IISExe;
_iisProcess.StartInfo.Arguments = String.Format("/config:\"{0}\" /trace:i", IISConfigPath);//"/config:"""" /trace:i";
_iisProcess.StartInfo.WorkingDirectory = _configProvider.ApplicationRoot;
_iisProcess.StartInfo.UseShellExecute = false; var startInfo = new ProcessStartInfo();
_iisProcess.StartInfo.RedirectStandardOutput = true;
_iisProcess.StartInfo.RedirectStandardError = true;
_iisProcess.StartInfo.CreateNoWindow = true;
startInfo.FileName = _configProvider.IISExePath;
startInfo.Arguments = String.Format("/config:\"{0}\" /trace:i", _configProvider.IISConfigPath);
startInfo.WorkingDirectory = _configProvider.ApplicationRoot;
_iisProcess.OutputDataReceived += (OnOutputDataReceived); startInfo.UseShellExecute = false;
_iisProcess.ErrorDataReceived += (OnErrorDataReceived); startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardError = true;
startInfo.CreateNoWindow = true;
//Set Variables for the config file. //Set Variables for the config file.
_iisProcess.StartInfo.EnvironmentVariables.Add("NZBDRONE_PATH", _configProvider.ApplicationRoot); startInfo.EnvironmentVariables.Add("NZBDRONE_PATH", _configProvider.ApplicationRoot);
_iisProcess.StartInfo.EnvironmentVariables.Add("NZBDRONE_PID", Process.GetCurrentProcess().Id.ToString()); startInfo.EnvironmentVariables.Add("NZBDRONE_PID", Process.GetCurrentProcess().Id.ToString());
try try
{ {
UpdateIISConfig(); _configProvider.UpdateIISConfig(_configProvider.IISConfigPath);
} }
catch (Exception e) catch (Exception e)
{ {
Logger.ErrorException("An error has occurred while trying to update the config file.", e); Logger.ErrorException("An error has occurred while trying to update the config file.", e);
} }
var iisProcess = _processProvider.Start(startInfo);
IISProcessId = iisProcess.Id;
Logger.Info("Starting process. [{0}]", _iisProcess.StartInfo.FileName); iisProcess.OutputDataReceived += (OnOutputDataReceived);
iisProcess.ErrorDataReceived += (OnErrorDataReceived);
_iisProcess.Start(); iisProcess.BeginErrorReadLine();
_iisProcess.PriorityClass = ProcessPriorityClass.AboveNormal; iisProcess.BeginOutputReadLine();
_iisProcess.BeginErrorReadLine(); ServerStarted = true;
_iisProcess.BeginOutputReadLine();
//Start Ping
_pingTimer = new Timer(300000) { AutoReset = true };
_pingTimer.Elapsed += (PingServer);
_pingTimer.Start();
return _iisProcess;
} }
private static void OnErrorDataReceived(object sender, DataReceivedEventArgs e) private static void OnErrorDataReceived(object sender, DataReceivedEventArgs e)
@ -109,19 +84,18 @@ namespace NzbDrone.Providers
IISLogger.Error(e.Data); IISLogger.Error(e.Data);
} }
internal void StopServer() public void StopServer()
{ {
KillProcess(_iisProcess); _processProvider.Kill(IISProcessId);
Logger.Info("Finding orphaned IIS Processes."); Logger.Info("Finding orphaned IIS Processes.");
foreach (var process in Process.GetProcessesByName("IISExpress")) foreach (var process in _processProvider.GetProcessByName("IISExpress"))
{ {
string processPath = process.MainModule.FileName; Logger.Info("[{0}]IIS Process found. Path:{1}", process.Id, process.StartPath);
Logger.Info("[{0}]IIS Process found. Path:{1}", process.Id, processPath); if (NormalizePath(process.StartPath) == NormalizePath(_configProvider.IISExePath))
if (NormalizePath(processPath) == NormalizePath(IISExe))
{ {
Logger.Info("[{0}]Process is considered orphaned.", process.Id); Logger.Info("[{0}]Process is considered orphaned.", process.Id);
KillProcess(process); _processProvider.Kill(process.Id);
} }
else else
{ {
@ -130,41 +104,14 @@ namespace NzbDrone.Providers
} }
} }
private void RestartServer() public void RestartServer()
{ {
_pingTimer.Stop(); ServerStarted = false;
Logger.Warn("Attempting to restart server."); Logger.Warn("Attempting to restart server.");
StopServer(); StopServer();
StartServer(); StartServer();
} }
private void PingServer(object sender, ElapsedEventArgs e)
{
try
{
var response = new WebClient().DownloadString(AppUrl + "/health");
if (!response.Contains("OK"))
{
throw new ServerException("Health services responded with an invalid response.");
}
if (_pingFailCounter > 0)
{
Logger.Info("Application pool has been successfully recovered.");
}
_pingFailCounter = 0;
}
catch (Exception ex)
{
_pingFailCounter++;
Logger.ErrorException("Application pool is not responding. Count " + _pingFailCounter, ex);
if (_pingFailCounter > 2)
{
RestartServer();
}
}
}
private void OnOutputDataReceived(object s, DataReceivedEventArgs e) private void OnOutputDataReceived(object s, DataReceivedEventArgs e)
{ {
if (e == null || String.IsNullOrWhiteSpace(e.Data) || e.Data.StartsWith("Request started:") || if (e == null || String.IsNullOrWhiteSpace(e.Data) || e.Data.StartsWith("Request started:") ||
@ -180,47 +127,7 @@ namespace NzbDrone.Providers
IISLogger.Trace(e.Data); IISLogger.Trace(e.Data);
} }
private void UpdateIISConfig() private string NormalizePath(string path)
{
string configPath = Path.Combine(_configProvider.IISFolder, @"AppServer\applicationhost.config");
Logger.Info(@"Server configuration file: {0}", configPath);
Logger.Info(@"Configuring server to: [http://localhost:{0}]", _configProvider.Port);
var configXml = XDocument.Load(configPath);
var bindings =
configXml.XPathSelectElement("configuration/system.applicationHost/sites").Elements("site").Where(
d => d.Attribute("name").Value.ToLowerInvariant() == "nzbdrone").First().Element("bindings");
bindings.Descendants().Remove();
bindings.Add(
new XElement("binding",
new XAttribute("protocol", "http"),
new XAttribute("bindingInformation", String.Format("*:{0}:localhost", _configProvider.Port))
));
bindings.Add(
new XElement("binding",
new XAttribute("protocol", "http"),
new XAttribute("bindingInformation", String.Format("*:{0}:", _configProvider.Port))
));
configXml.Save(configPath);
}
private void KillProcess(Process process)
{
if (process != null && !process.HasExited)
{
Logger.Info("[{0}]Killing process", process.Id);
process.Kill();
Logger.Info("[{0}]Waiting for exit", process.Id);
process.WaitForExit();
Logger.Info("[{0}]Process terminated successfully", process.Id);
}
}
public string NormalizePath(string path)
{ {
if (String.IsNullOrWhiteSpace(path)) if (String.IsNullOrWhiteSpace(path))
throw new ArgumentException("Path can not be null or empty"); throw new ArgumentException("Path can not be null or empty");

@ -0,0 +1,115 @@
using System;
using System.Diagnostics;
using System.Net;
using System.Runtime.Remoting;
using System.Timers;
using NLog;
using Ninject;
namespace NzbDrone.Providers
{
public class MonitoringProvider
{
private static readonly Logger Logger = LogManager.GetLogger("MonitoringProvider");
private readonly IISProvider _iisProvider;
private readonly ProcessProvider _processProvider;
private int _pingFailCounter;
private Timer _pingTimer;
[Inject]
public MonitoringProvider(ProcessProvider processProvider, IISProvider iisProvider)
{
_processProvider = processProvider;
_iisProvider = iisProvider;
}
public void Start()
{
AppDomain.CurrentDomain.UnhandledException += ((s, e) => AppDomainException(e));
AppDomain.CurrentDomain.ProcessExit += ProgramExited;
AppDomain.CurrentDomain.DomainUnload += ProgramExited;
var prioCheckTimer = new Timer(5000);
prioCheckTimer.Elapsed += EnsurePriority;
prioCheckTimer.Enabled = true;
_pingTimer = new Timer(60000) { AutoReset = true };
_pingTimer.Elapsed += (PingServer);
_pingTimer.Start();
}
public MonitoringProvider()
{
}
public virtual void EnsurePriority(object sender, ElapsedEventArgs e)
{
var currentProcess = _processProvider.GetCurrentProcess();
if (currentProcess.Priority != ProcessPriorityClass.Normal)
{
_processProvider.SetPriority(currentProcess.Id, ProcessPriorityClass.Normal);
}
var iisProcess = _processProvider.GetProcessById(_iisProvider.IISProcessId);
if (iisProcess != null && iisProcess.Priority != ProcessPriorityClass.Normal &&
iisProcess.Priority != ProcessPriorityClass.AboveNormal)
{
_processProvider.SetPriority(iisProcess.Id, ProcessPriorityClass.Normal);
}
}
public virtual void PingServer(object sender, ElapsedEventArgs e)
{
if (!_iisProvider.ServerStarted) return;
try
{
string response = new WebClient().DownloadString(_iisProvider.AppUrl + "/health");
if (!response.Contains("OK"))
{
throw new ServerException("Health services responded with an invalid response.");
}
if (_pingFailCounter > 0)
{
Logger.Info("Application pool has been successfully recovered.");
}
_pingFailCounter = 0;
}
catch (Exception ex)
{
_pingFailCounter++;
Logger.ErrorException("Application pool is not responding. Count " + _pingFailCounter, ex);
if (_pingFailCounter > 2)
{
_iisProvider.RestartServer();
}
}
}
private void ProgramExited(object sender, EventArgs e)
{
_iisProvider.StopServer();
}
private static void AppDomainException(object excepion)
{
Console.WriteLine("EPIC FAIL: {0}", excepion);
Logger.Fatal("EPIC FAIL: {0}", excepion);
#if RELEASE
new Client
{
ApiKey = "43BBF60A-EB2A-4C1C-B09E-422ADF637265",
ApplicationName = "NZBDrone",
CurrentException = excepion as Exception
}.Submit();
#endif
}
}
}

@ -1,8 +1,6 @@
using System; using System.Collections.Generic;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Text;
using NLog; using NLog;
namespace NzbDrone.Providers namespace NzbDrone.Providers
@ -12,31 +10,77 @@ namespace NzbDrone.Providers
private static readonly Logger Logger = LogManager.GetLogger("ProcessProvider"); private static readonly Logger Logger = LogManager.GetLogger("ProcessProvider");
public virtual void SetPriority(int processId, ProcessPriorityClass priority) public virtual ProcessInfo GetCurrentProcess()
{ {
var process = Process.GetProcessById(processId); return ConvertToProcessInfo(Process.GetCurrentProcess());
}
Logger.Info("Updating [{0}] process priority from {1} to {2}", public virtual ProcessInfo GetProcessById(int id)
process.ProcessName, {
process.PriorityClass, return ConvertToProcessInfo(Process.GetProcesses().Where(p => p.Id == id).FirstOrDefault());
priority); }
process.PriorityClass = priority; public virtual IEnumerable<ProcessInfo> GetProcessByName(string name)
{
return Process.GetProcessesByName(name).Select(ConvertToProcessInfo);
} }
public virtual ProcessPriorityClass GetProcessPriority(int processId) public virtual void Start(string path)
{ {
return Process.GetProcessById(processId).PriorityClass; Process.Start(path);
} }
public virtual int GetCurrentProcessId() public virtual Process Start(ProcessStartInfo startInfo)
{ {
return Process.GetCurrentProcess().Id; Logger.Info("Starting process. [{0}]", startInfo.FileName);
var process = new Process
{
StartInfo = startInfo
};
process.Start();
return process;
} }
public virtual Process Start(string path) public virtual void Kill(int processId)
{ {
return Process.Start(path); if (processId == 0) return;
if (!Process.GetProcesses().Any(p => p.Id == processId)) return;
var process = Process.GetProcessById(processId);
if (!process.HasExited)
{
Logger.Info("[{0}]Killing process", process.Id);
process.Kill();
Logger.Info("[{0}]Waiting for exit", process.Id);
process.WaitForExit();
Logger.Info("[{0}]Process terminated successfully", process.Id);
}
}
public virtual void SetPriority(int processId, ProcessPriorityClass priority)
{
var process = Process.GetProcessById(processId);
Logger.Info("Updating [{0}] process priority from {1} to {2}",
process.ProcessName,
process.PriorityClass,
priority);
process.PriorityClass = priority;
}
private static ProcessInfo ConvertToProcessInfo(Process process)
{
if (process == null) return null;
return new ProcessInfo
{
Id = process.Id,
Priority = process.PriorityClass,
StartPath = process.MainModule.FileName
};
} }
} }
} }

@ -6,7 +6,7 @@ using System.Text;
namespace NzbDrone.Providers namespace NzbDrone.Providers
{ {
internal class WebClientProvider public class WebClientProvider
{ {
public virtual string DownloadString(string url) public virtual string DownloadString(string url)

Binary file not shown.
Loading…
Cancel
Save