From a1be3efecb6aec67253459cb45937e42d6e4f459 Mon Sep 17 00:00:00 2001 From: Renaud Paquay Date: Fri, 4 Jun 2010 19:13:57 -0700 Subject: [PATCH] Module loading strategies Make the precompiled and dynamic extension loaders work properly and also refactor some code to introduce proper abstractions, such as IVirtualPathProvider, IHostEnvironment, IBuildManager, etc. --HG-- branch : dev --- src/Orchard.Web/Web.config | 2 +- .../CSharpExtensionBuildProvider.cs | 4 +- .../CSharpExtensionCompiler.cs | 3 +- .../CSharpProjectFullTrustCompiler.cs | 15 +-- .../CSharpProjectMediumTrustCompiler.cs | 6 +- .../CSharpProjectParser.cs | 21 +++- .../Extensions/Loaders/AreaExtensionLoader.cs | 2 +- .../Extensions/Loaders/CoreExtensionLoader.cs | 14 ++- .../Loaders/DynamicExtensionLoader.cs | 78 ++++--------- .../Extensions/Loaders/IBuildManager.cs | 16 --- .../Loaders/IVirtualPathProvider.cs | 30 ----- .../Loaders/PrecompiledExtensionLoader.cs | 38 ++++-- .../Loaders/ProbingExtensionLoader.cs | 30 +++++ .../Loaders/ReferencedExtensionLoader.cs | 13 ++- .../Loaders => }/IAssemblyBuilder.cs | 2 +- src/Orchard/Environment/IBuildManager.cs | 21 ++++ src/Orchard/Environment/IHostEnvironment.cs | 22 ++++ .../Environment/IVirtualPathProvider.cs | 50 ++++++++ src/Orchard/Environment/OrchardStarter.cs | 6 + .../Dependencies/IDependenciesFolder.cs | 110 ++++++++++++++++++ src/Orchard/Orchard.Framework.csproj | 19 +-- 21 files changed, 355 insertions(+), 147 deletions(-) rename src/Orchard/Environment/Extensions/{Loaders => Compilers}/CSharpExtensionBuildProvider.cs (83%) rename src/Orchard/Environment/Extensions/{Loaders => Compilers}/CSharpExtensionCompiler.cs (93%) rename src/Orchard/Environment/Extensions/{Loaders => Compilers}/CSharpProjectFullTrustCompiler.cs (77%) rename src/Orchard/Environment/Extensions/{Loaders => Compilers}/CSharpProjectMediumTrustCompiler.cs (93%) rename src/Orchard/Environment/Extensions/{Loaders => Compilers}/CSharpProjectParser.cs (66%) delete mode 100644 src/Orchard/Environment/Extensions/Loaders/IBuildManager.cs delete mode 100644 src/Orchard/Environment/Extensions/Loaders/IVirtualPathProvider.cs create mode 100644 src/Orchard/Environment/Extensions/Loaders/ProbingExtensionLoader.cs rename src/Orchard/Environment/{Extensions/Loaders => }/IAssemblyBuilder.cs (90%) create mode 100644 src/Orchard/Environment/IBuildManager.cs create mode 100644 src/Orchard/Environment/IHostEnvironment.cs create mode 100644 src/Orchard/Environment/IVirtualPathProvider.cs create mode 100644 src/Orchard/FileSystems/Dependencies/IDependenciesFolder.cs diff --git a/src/Orchard.Web/Web.config b/src/Orchard.Web/Web.config index 3413572ba..df511b385 100644 --- a/src/Orchard.Web/Web.config +++ b/src/Orchard.Web/Web.config @@ -28,7 +28,7 @@ --> - + diff --git a/src/Orchard/Environment/Extensions/Loaders/CSharpExtensionBuildProvider.cs b/src/Orchard/Environment/Extensions/Compilers/CSharpExtensionBuildProvider.cs similarity index 83% rename from src/Orchard/Environment/Extensions/Loaders/CSharpExtensionBuildProvider.cs rename to src/Orchard/Environment/Extensions/Compilers/CSharpExtensionBuildProvider.cs index 92ce7357f..826eccc64 100644 --- a/src/Orchard/Environment/Extensions/Loaders/CSharpExtensionBuildProvider.cs +++ b/src/Orchard/Environment/Extensions/Compilers/CSharpExtensionBuildProvider.cs @@ -1,6 +1,6 @@ using System.Web.Compilation; -namespace Orchard.Environment.Extensions.Loaders { +namespace Orchard.Environment.Extensions.Compilers { public class CSharpExtensionBuildProvider : BuildProvider { private readonly CompilerType _codeCompilerType; @@ -11,7 +11,7 @@ namespace Orchard.Environment.Extensions.Loaders { public override CompilerType CodeCompilerType { get { return _codeCompilerType; } } public override void GenerateCode(AssemblyBuilder assemblyBuilder) { - var virtualPathProvider = new AspNetVirtualPathProvider(); + var virtualPathProvider = new DefaultVirtualPathProvider(); var compiler = new CSharpProjectMediumTrustCompiler(virtualPathProvider); var aspNetAssemblyBuilder = new AspNetAssemblyBuilder(assemblyBuilder, this); diff --git a/src/Orchard/Environment/Extensions/Loaders/CSharpExtensionCompiler.cs b/src/Orchard/Environment/Extensions/Compilers/CSharpExtensionCompiler.cs similarity index 93% rename from src/Orchard/Environment/Extensions/Loaders/CSharpExtensionCompiler.cs rename to src/Orchard/Environment/Extensions/Compilers/CSharpExtensionCompiler.cs index b4f6d380a..b267831db 100644 --- a/src/Orchard/Environment/Extensions/Loaders/CSharpExtensionCompiler.cs +++ b/src/Orchard/Environment/Extensions/Compilers/CSharpExtensionCompiler.cs @@ -4,9 +4,8 @@ using System.IO; using System.Linq; using System.Reflection; using System.Web.Compilation; -using System.Web.Hosting; -namespace Orchard.Environment.Extensions.Loaders { +namespace Orchard.Environment.Extensions.Compilers { /// /// Compile a C# extension into an assembly given a directory location /// diff --git a/src/Orchard/Environment/Extensions/Loaders/CSharpProjectFullTrustCompiler.cs b/src/Orchard/Environment/Extensions/Compilers/CSharpProjectFullTrustCompiler.cs similarity index 77% rename from src/Orchard/Environment/Extensions/Loaders/CSharpProjectFullTrustCompiler.cs rename to src/Orchard/Environment/Extensions/Compilers/CSharpProjectFullTrustCompiler.cs index 4977434a2..d8d0ced8c 100644 --- a/src/Orchard/Environment/Extensions/Loaders/CSharpProjectFullTrustCompiler.cs +++ b/src/Orchard/Environment/Extensions/Compilers/CSharpProjectFullTrustCompiler.cs @@ -1,9 +1,9 @@ using System.CodeDom.Compiler; using System.Collections.Generic; +using System.IO; using System.Linq; -using System.Reflection; -namespace Orchard.Environment.Extensions.Loaders { +namespace Orchard.Environment.Extensions.Compilers { /// /// Compile a C# extension into an assembly given a directory location /// @@ -20,15 +20,17 @@ namespace Orchard.Environment.Extensions.Loaders { /// Compile a csproj file given its virtual path. Use the CSharp CodeDomProvider /// class, which is only available in full trust. /// - public CompilerResults CompileProject(string virtualPath) { + public CompilerResults CompileProject(string virtualPath, string outputDirectory) { var codeProvider = CodeDomProvider.CreateProvider("cs"); var directory = _virtualPathProvider.GetDirectoryName(virtualPath); using (var stream = _virtualPathProvider.OpenFile(virtualPath)) { var descriptor = new CSharpProjectParser().Parse(stream); - var references = GetAssemblyReferenceNames(); + var references = GetReferencedAssembliesLocation(); var options = new CompilerParameters(references.ToArray()); + options.GenerateExecutable = false; + options.OutputAssembly = Path.Combine(outputDirectory, descriptor.AssemblyName + ".dll"); var fileNames = descriptor.SourceFilenames .Select(f => _virtualPathProvider.Combine(directory, f)) @@ -39,11 +41,10 @@ namespace Orchard.Environment.Extensions.Loaders { } } - private IEnumerable GetAssemblyReferenceNames() { + private IEnumerable GetReferencedAssembliesLocation() { return _buildManager.GetReferencedAssemblies() - .OfType() - .Where(a => !string.IsNullOrEmpty(a.Location)) .Select(a => a.Location) + .Where(a => !string.IsNullOrEmpty(a)) .Distinct(); } } diff --git a/src/Orchard/Environment/Extensions/Loaders/CSharpProjectMediumTrustCompiler.cs b/src/Orchard/Environment/Extensions/Compilers/CSharpProjectMediumTrustCompiler.cs similarity index 93% rename from src/Orchard/Environment/Extensions/Loaders/CSharpProjectMediumTrustCompiler.cs rename to src/Orchard/Environment/Extensions/Compilers/CSharpProjectMediumTrustCompiler.cs index b8d611d6e..1ab94e637 100644 --- a/src/Orchard/Environment/Extensions/Loaders/CSharpProjectMediumTrustCompiler.cs +++ b/src/Orchard/Environment/Extensions/Compilers/CSharpProjectMediumTrustCompiler.cs @@ -1,10 +1,8 @@ -using System; -using System.CodeDom; +using System.CodeDom; using System.IO; using System.Linq; - -namespace Orchard.Environment.Extensions.Loaders { +namespace Orchard.Environment.Extensions.Compilers { /// /// Compile a C# extension into an assembly given a directory location /// diff --git a/src/Orchard/Environment/Extensions/Loaders/CSharpProjectParser.cs b/src/Orchard/Environment/Extensions/Compilers/CSharpProjectParser.cs similarity index 66% rename from src/Orchard/Environment/Extensions/Loaders/CSharpProjectParser.cs rename to src/Orchard/Environment/Extensions/Compilers/CSharpProjectParser.cs index d367c0886..1c0288149 100644 --- a/src/Orchard/Environment/Extensions/Loaders/CSharpProjectParser.cs +++ b/src/Orchard/Environment/Extensions/Compilers/CSharpProjectParser.cs @@ -4,25 +4,40 @@ using System.Linq; using System.Xml; using System.Xml.Linq; -namespace Orchard.Environment.Extensions.Loaders { +namespace Orchard.Environment.Extensions.Compilers { public class CSharpProjectDescriptor { + public string AssemblyName { get; set; } public IEnumerable SourceFilenames { get; set; } public IEnumerable References { get; set; } } public class ReferenceDescriptor { public string AssemblyName { get; set; } + + public override string ToString() { + return "{" + (AssemblyName ?? "") + "}"; + } } public class CSharpProjectParser { public CSharpProjectDescriptor Parse(Stream stream) { var document = XDocument.Load(XmlReader.Create(stream)); return new CSharpProjectDescriptor { - SourceFilenames = GetSourceFilenames(document), - References = GetReferences(document) + AssemblyName = GetAssemblyName(document), + SourceFilenames = GetSourceFilenames(document).ToArray(), + References = GetReferences(document).ToArray() }; } + private string GetAssemblyName(XDocument document) { + return document + .Elements(ns("Project")) + .Elements(ns("PropertyGroup")) + .Elements(ns("AssemblyName")) + .Single() + .Value; + } + private IEnumerable GetSourceFilenames(XDocument document) { return document .Elements(ns("Project")) diff --git a/src/Orchard/Environment/Extensions/Loaders/AreaExtensionLoader.cs b/src/Orchard/Environment/Extensions/Loaders/AreaExtensionLoader.cs index 181c32dbf..5323212b7 100644 --- a/src/Orchard/Environment/Extensions/Loaders/AreaExtensionLoader.cs +++ b/src/Orchard/Environment/Extensions/Loaders/AreaExtensionLoader.cs @@ -5,7 +5,7 @@ using Orchard.Environment.Extensions.Models; namespace Orchard.Environment.Extensions.Loaders { public class AreaExtensionLoader : IExtensionLoader { - public int Order { get { return 5; } } + public int Order { get { return 50; } } public ExtensionEntry Load(ExtensionDescriptor descriptor) { if (descriptor.Location == "~/Areas") { diff --git a/src/Orchard/Environment/Extensions/Loaders/CoreExtensionLoader.cs b/src/Orchard/Environment/Extensions/Loaders/CoreExtensionLoader.cs index 856df10f0..ba36c7f6c 100644 --- a/src/Orchard/Environment/Extensions/Loaders/CoreExtensionLoader.cs +++ b/src/Orchard/Environment/Extensions/Loaders/CoreExtensionLoader.cs @@ -4,18 +4,20 @@ using System.Reflection; using Orchard.Environment.Extensions.Models; namespace Orchard.Environment.Extensions.Loaders { + /// + /// Load an extension by looking into specific namespaces of the "Orchard.Core" assembly + /// public class CoreExtensionLoader : IExtensionLoader { - public int Order { get { return 1; } } + public int Order { get { return 10; } } public ExtensionEntry Load(ExtensionDescriptor descriptor) { if (descriptor.Location == "~/Core") { - var assembly = Assembly.Load("Orchard.Core"); return new ExtensionEntry { - Descriptor = descriptor, - Assembly = assembly, - ExportedTypes = assembly.GetExportedTypes().Where(x => IsTypeFromModule(x, descriptor)) - }; + Descriptor = descriptor, + Assembly = assembly, + ExportedTypes = assembly.GetExportedTypes().Where(x => IsTypeFromModule(x, descriptor)) + }; } return null; } diff --git a/src/Orchard/Environment/Extensions/Loaders/DynamicExtensionLoader.cs b/src/Orchard/Environment/Extensions/Loaders/DynamicExtensionLoader.cs index 64ca1a891..fd000783c 100644 --- a/src/Orchard/Environment/Extensions/Loaders/DynamicExtensionLoader.cs +++ b/src/Orchard/Environment/Extensions/Loaders/DynamicExtensionLoader.cs @@ -1,64 +1,36 @@ using System; -using System.IO; -using System.Reflection; -using System.Web.Compilation; -using System.Web.Hosting; using Orchard.Environment.Extensions.Models; +using Orchard.FileSystems.Dependencies; namespace Orchard.Environment.Extensions.Loaders { public class DynamicExtensionLoader : IExtensionLoader { - public int Order { get { return 10; } } + private readonly IHostEnvironment _hostEnvironment; + private readonly IBuildManager _buildManager; + private readonly IVirtualPathProvider _virtualPathProvider; + private readonly IDependenciesFolder _dependenciesFolder; - public ExtensionEntry Load(ExtensionDescriptor descriptor) { - if (HostingEnvironment.IsHosted == false) - return null; - - // 1) Try to load the assembly directory - // This will look in the "App_Data/Dependencies" directory if - // the probing path is correctly configured in Web.config - { - try { - Assembly assembly = Assembly.Load(descriptor.Name); - return CreateExtensionEntry(descriptor, assembly); - } - catch (FileNotFoundException e) { - // The assembly is not in one of the probing directory, - // including "App_Data/Dependencies", we need to move on - // to other strageties - } - } - - // 2) look for the assembly in the "Bin" directory - { - string modulePath = HostingEnvironment.MapPath(descriptor.Location); - modulePath = Path.Combine(modulePath, descriptor.Name); - string moduleBinary = Path.Combine(modulePath, "bin"); - moduleBinary = Path.Combine(moduleBinary, descriptor.Name + ".dll"); - if (File.Exists(moduleBinary)) { - // Copy file to dependencies directory - string dependenciesPath = HostingEnvironment.MapPath("~/App_Data/Dependencies"); - if (!Directory.Exists(dependenciesPath)) { - Directory.CreateDirectory(dependenciesPath); - } - string destFile = Path.Combine(dependenciesPath, descriptor.Name + ".dll"); - File.Copy(moduleBinary, destFile, true); - - // then load the assembly - Assembly assembly = Assembly.Load(descriptor.Name); - return CreateExtensionEntry(descriptor, assembly); - } - } - - // 3) look for the csproj in the module directory - { - string projfileName = Path.Combine(descriptor.Location, descriptor.Name); - projfileName = Path.Combine(projfileName, descriptor.Name + ".csproj").Replace('\\', '/'); - var assembly = BuildManager.GetCompiledAssembly(projfileName); - return CreateExtensionEntry(descriptor, assembly); - } + public DynamicExtensionLoader(IHostEnvironment hostEnvironment, IBuildManager buildManager, IVirtualPathProvider virtualPathProvider, IDependenciesFolder dependenciesFolder) { + _hostEnvironment = hostEnvironment; + _buildManager = buildManager; + _virtualPathProvider = virtualPathProvider; + _dependenciesFolder = dependenciesFolder; } - private static ExtensionEntry CreateExtensionEntry(ExtensionDescriptor descriptor, Assembly assembly) { + public int Order { get { return 100; } } + + public ExtensionEntry Load(ExtensionDescriptor descriptor) { + string projectPath = _virtualPathProvider.Combine(descriptor.Location, descriptor.Name, + descriptor.Name + ".csproj"); + if (!_virtualPathProvider.FileExists(projectPath)) { + return null; + } + + var assembly = _buildManager.GetCompiledAssembly(projectPath); + + if (_hostEnvironment.IsFullTrust) { + _dependenciesFolder.StoreAssemblyFile(descriptor.Name, assembly.Location); + } + return new ExtensionEntry { Descriptor = descriptor, Assembly = assembly, diff --git a/src/Orchard/Environment/Extensions/Loaders/IBuildManager.cs b/src/Orchard/Environment/Extensions/Loaders/IBuildManager.cs deleted file mode 100644 index af638f9d6..000000000 --- a/src/Orchard/Environment/Extensions/Loaders/IBuildManager.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Linq; -using System.Collections.Generic; -using System.Web.Compilation; - -namespace Orchard.Environment.Extensions.Loaders { - public interface IBuildManager { - IEnumerable GetReferencedAssemblies(); - } - - public class AspNetBuildManager : IBuildManager { - public IEnumerable GetReferencedAssemblies() { - return BuildManager.GetReferencedAssemblies().Cast(); - } - } -} \ No newline at end of file diff --git a/src/Orchard/Environment/Extensions/Loaders/IVirtualPathProvider.cs b/src/Orchard/Environment/Extensions/Loaders/IVirtualPathProvider.cs deleted file mode 100644 index 79f7d0a9e..000000000 --- a/src/Orchard/Environment/Extensions/Loaders/IVirtualPathProvider.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.IO; -using System.Web.Hosting; - -namespace Orchard.Environment.Extensions.Loaders { - public interface IVirtualPathProvider { - string GetDirectoryName(string virtualPath); - string Combine(params string[] paths); - Stream OpenFile(string virtualPath); - string MapPath(string virtualPath); - } - - public class AspNetVirtualPathProvider : IVirtualPathProvider { - public string GetDirectoryName(string virtualPath) { - return Path.GetDirectoryName(virtualPath); - } - - public string Combine(params string[] paths) { - return Path.Combine(paths).Replace('\\', '/'); - } - - public Stream OpenFile(string virtualPath) { - return VirtualPathProvider.OpenFile(virtualPath); - } - - public string MapPath(string virtualPath) { - return HostingEnvironment.MapPath(virtualPath); - } - } -} \ No newline at end of file diff --git a/src/Orchard/Environment/Extensions/Loaders/PrecompiledExtensionLoader.cs b/src/Orchard/Environment/Extensions/Loaders/PrecompiledExtensionLoader.cs index 3f7a30ee2..5d24d8205 100644 --- a/src/Orchard/Environment/Extensions/Loaders/PrecompiledExtensionLoader.cs +++ b/src/Orchard/Environment/Extensions/Loaders/PrecompiledExtensionLoader.cs @@ -1,17 +1,39 @@ using Orchard.Environment.Extensions.Models; +using Orchard.FileSystems.Dependencies; namespace Orchard.Environment.Extensions.Loaders { + /// + /// Load an extension by looking into the "bin" subdirectory of an + /// extension directory. + /// public class PrecompiledExtensionLoader : IExtensionLoader { - public int Order { get { return 3; } } + private readonly IDependenciesFolder _folder; + private readonly IVirtualPathProvider _virtualPathProvider; + + public PrecompiledExtensionLoader(IDependenciesFolder folder, IVirtualPathProvider virtualPathProvider) { + _folder = folder; + _virtualPathProvider = virtualPathProvider; + } + + public int Order { get { return 40; } } public ExtensionEntry Load(ExtensionDescriptor descriptor) { - //var assembly = Assembly.Load(descriptor.Name); - //return new ModuleEntry { - // Descriptor = descriptor, - // Assembly = assembly, - // ExportedTypes = assembly.GetExportedTypes() - //}; - return null; + var extensionPath = _virtualPathProvider.Combine(descriptor.Location, descriptor.Name, "bin", + descriptor.Name + ".dll"); + if (!_virtualPathProvider.FileExists(extensionPath)) + return null; + + _folder.StoreAssemblyFile(descriptor.Name, _virtualPathProvider.MapPath(extensionPath)); + + var assembly = _folder.LoadAssembly(descriptor.Name); + if (assembly == null) + return null; + + return new ExtensionEntry { + Descriptor = descriptor, + Assembly = assembly, + ExportedTypes = assembly.GetExportedTypes() + }; } } } \ No newline at end of file diff --git a/src/Orchard/Environment/Extensions/Loaders/ProbingExtensionLoader.cs b/src/Orchard/Environment/Extensions/Loaders/ProbingExtensionLoader.cs new file mode 100644 index 000000000..12b40c516 --- /dev/null +++ b/src/Orchard/Environment/Extensions/Loaders/ProbingExtensionLoader.cs @@ -0,0 +1,30 @@ +using Orchard.Environment.Extensions.Models; +using Orchard.FileSystems.Dependencies; + +namespace Orchard.Environment.Extensions.Loaders { + /// + /// Load an extension using the "Assembly.Load" method if the + /// file can be found in the "App_Data/Dependencies" folder. + /// + public class ProbingExtensionLoader : IExtensionLoader { + private readonly IDependenciesFolder _folder; + + public ProbingExtensionLoader(IDependenciesFolder folder) { + _folder = folder; + } + + public int Order { get { return 30; } } + + public ExtensionEntry Load(ExtensionDescriptor descriptor) { + var assembly = _folder.LoadAssembly(descriptor.Name); + if (assembly == null) + return null; + + return new ExtensionEntry { + Descriptor = descriptor, + Assembly = assembly, + ExportedTypes = assembly.GetExportedTypes() + }; + } + } +} \ No newline at end of file diff --git a/src/Orchard/Environment/Extensions/Loaders/ReferencedExtensionLoader.cs b/src/Orchard/Environment/Extensions/Loaders/ReferencedExtensionLoader.cs index 4aa7ce345..39b8a19f0 100644 --- a/src/Orchard/Environment/Extensions/Loaders/ReferencedExtensionLoader.cs +++ b/src/Orchard/Environment/Extensions/Loaders/ReferencedExtensionLoader.cs @@ -5,8 +5,11 @@ using System.Web.Hosting; using Orchard.Environment.Extensions.Models; namespace Orchard.Environment.Extensions.Loaders { + /// + /// Load an extension by looking through the BuildManager referenced assemblies + /// public class ReferencedExtensionLoader : IExtensionLoader { - public int Order { get { return 2; } } + public int Order { get { return 20; } } public ExtensionEntry Load(ExtensionDescriptor descriptor) { if (HostingEnvironment.IsHosted == false) @@ -20,10 +23,10 @@ namespace Orchard.Environment.Extensions.Loaders { return null; return new ExtensionEntry { - Descriptor = descriptor, - Assembly = assembly, - ExportedTypes = assembly.GetExportedTypes() - }; + Descriptor = descriptor, + Assembly = assembly, + ExportedTypes = assembly.GetExportedTypes() + }; } } } \ No newline at end of file diff --git a/src/Orchard/Environment/Extensions/Loaders/IAssemblyBuilder.cs b/src/Orchard/Environment/IAssemblyBuilder.cs similarity index 90% rename from src/Orchard/Environment/Extensions/Loaders/IAssemblyBuilder.cs rename to src/Orchard/Environment/IAssemblyBuilder.cs index b2e434f33..055fb1caa 100644 --- a/src/Orchard/Environment/Extensions/Loaders/IAssemblyBuilder.cs +++ b/src/Orchard/Environment/IAssemblyBuilder.cs @@ -1,7 +1,7 @@ using System.CodeDom; using System.Web.Compilation; -namespace Orchard.Environment.Extensions.Loaders { +namespace Orchard.Environment { public interface IAssemblyBuilder { void AddCodeCompileUnit(CodeCompileUnit compileUnit); } diff --git a/src/Orchard/Environment/IBuildManager.cs b/src/Orchard/Environment/IBuildManager.cs new file mode 100644 index 000000000..625e7190a --- /dev/null +++ b/src/Orchard/Environment/IBuildManager.cs @@ -0,0 +1,21 @@ +using System.Linq; +using System.Collections.Generic; +using System.Reflection; +using System.Web.Compilation; + +namespace Orchard.Environment { + public interface IBuildManager : IDependency { + IEnumerable GetReferencedAssemblies(); + Assembly GetCompiledAssembly(string virtualPath); + } + + public class DefaultBuildManager : IBuildManager { + public IEnumerable GetReferencedAssemblies() { + return BuildManager.GetReferencedAssemblies().OfType(); + } + + public Assembly GetCompiledAssembly(string virtualPath) { + return BuildManager.GetCompiledAssembly(virtualPath); + } + } +} \ No newline at end of file diff --git a/src/Orchard/Environment/IHostEnvironment.cs b/src/Orchard/Environment/IHostEnvironment.cs new file mode 100644 index 000000000..694bc5cc0 --- /dev/null +++ b/src/Orchard/Environment/IHostEnvironment.cs @@ -0,0 +1,22 @@ +using System; +using System.Web.Hosting; + +namespace Orchard.Environment { + /// + /// Abstraction of the running environment + /// + public interface IHostEnvironment : IDependency { + bool IsFullTrust { get; } + string MapPath(string virtualPath); + } + + public class DefaultHostEnvironment : IHostEnvironment { + public bool IsFullTrust { + get { return AppDomain.CurrentDomain.IsFullyTrusted; } + } + + public string MapPath(string virtualPath) { + return HostingEnvironment.MapPath(virtualPath); + } + } +} diff --git a/src/Orchard/Environment/IVirtualPathProvider.cs b/src/Orchard/Environment/IVirtualPathProvider.cs new file mode 100644 index 000000000..37b824e39 --- /dev/null +++ b/src/Orchard/Environment/IVirtualPathProvider.cs @@ -0,0 +1,50 @@ +using System.IO; +using System.Web.Hosting; +using Orchard.Caching; + +namespace Orchard.Environment { + public interface IVirtualPathProvider : IVolatileProvider { + string GetDirectoryName(string virtualPath); + string Combine(params string[] paths); + Stream OpenFile(string virtualPath); + StreamWriter CreateText(string virtualPath); + string MapPath(string virtualPath); + bool FileExists(string virtualPath); + bool DirectoryExists(string virtualPath); + void CreateDirectory(string virtualPath); + } + + public class DefaultVirtualPathProvider : IVirtualPathProvider { + public string GetDirectoryName(string virtualPath) { + return Path.GetDirectoryName(virtualPath).Replace('\\', '/'); + } + + public string Combine(params string[] paths) { + return Path.Combine(paths).Replace('\\', '/'); + } + + public Stream OpenFile(string virtualPath) { + return HostingEnvironment.VirtualPathProvider.GetFile(virtualPath).Open(); + } + + public StreamWriter CreateText(string virtualPath) { + return File.CreateText(MapPath(virtualPath)); + } + + public string MapPath(string virtualPath) { + return HostingEnvironment.MapPath(virtualPath); + } + + public bool FileExists(string virtualPath) { + return HostingEnvironment.VirtualPathProvider.FileExists(virtualPath); + } + + public bool DirectoryExists(string virtualPath) { + return HostingEnvironment.VirtualPathProvider.DirectoryExists(virtualPath); + } + + public void CreateDirectory(string virtualPath) { + Directory.CreateDirectory(MapPath(virtualPath)); + } + } +} \ No newline at end of file diff --git a/src/Orchard/Environment/OrchardStarter.cs b/src/Orchard/Environment/OrchardStarter.cs index f8e6ec8f4..a86204858 100644 --- a/src/Orchard/Environment/OrchardStarter.cs +++ b/src/Orchard/Environment/OrchardStarter.cs @@ -15,6 +15,7 @@ using Orchard.Environment.State; using Orchard.Environment.Topology; using Orchard.Events; using Orchard.FileSystems.AppData; +using Orchard.FileSystems.Dependencies; using Orchard.FileSystems.WebSite; using Orchard.Logging; using Orchard.Services; @@ -30,10 +31,14 @@ namespace Orchard.Environment { // a single default host implementation is needed for bootstrapping a web app domain builder.RegisterType().As().SingleInstance(); builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As().SingleInstance(); RegisterVolatileProvider(builder); RegisterVolatileProvider(builder); RegisterVolatileProvider(builder); + RegisterVolatileProvider(builder); + RegisterVolatileProvider(builder); builder.RegisterType().As().As().SingleInstance(); { @@ -63,6 +68,7 @@ namespace Orchard.Environment { builder.RegisterType().As().SingleInstance(); builder.RegisterType().As().SingleInstance(); builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As().SingleInstance(); builder.RegisterType().As().SingleInstance(); } } diff --git a/src/Orchard/FileSystems/Dependencies/IDependenciesFolder.cs b/src/Orchard/FileSystems/Dependencies/IDependenciesFolder.cs new file mode 100644 index 000000000..1020c0ee4 --- /dev/null +++ b/src/Orchard/FileSystems/Dependencies/IDependenciesFolder.cs @@ -0,0 +1,110 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Xml.Linq; +using Orchard.Caching; +using Orchard.Environment; + +namespace Orchard.FileSystems.Dependencies { + public interface IDependenciesFolder : IVolatileProvider { + void StoreAssemblyFile(string assemblyName, string assemblyFileName); + Assembly LoadAssembly(string assemblyName); + } + + public class DefaultDependenciesFolder : IDependenciesFolder { + private const string _basePath = "~/App_Data/Dependencies"; + private readonly IVirtualPathProvider _virtualPathProvider; + + public DefaultDependenciesFolder(IVirtualPathProvider virtualPathProvider) { + _virtualPathProvider = virtualPathProvider; + } + + private string BasePath { + get { + return _basePath; + } + } + + private string PersistencePath { + get { + return _virtualPathProvider.Combine(BasePath, "dependencies.xml"); + } + } + + public void StoreAssemblyFile(string assemblyName, string assemblyFileName) { + StoreAssemblyFile(assemblyName, assemblyFileName, Path.GetFileName(assemblyFileName)); + } + + private void StoreAssemblyFile(string assemblyName, string assemblyFileName, string destinationFileName) { + _virtualPathProvider.CreateDirectory(BasePath); + + var destinationPath = _virtualPathProvider.MapPath(_virtualPathProvider.Combine(BasePath, destinationFileName)); + File.Copy(assemblyFileName, destinationPath); + + StoreDepencyInformation(assemblyName, destinationFileName); + } + + private void StoreDepencyInformation(string name, string fileName) { + var dependencies = ReadDependencies().ToList(); + + var dependency = dependencies.SingleOrDefault(d => d.Name == name); + if (dependency == null) { + dependency = new DependencyDescritpor { Name = name, FileName = fileName }; + dependencies.Add(dependency); + } + dependency.FileName = fileName; + + WriteDependencies(dependencies); + } + + public Assembly LoadAssembly(string assemblyName) { + _virtualPathProvider.CreateDirectory(BasePath); + + var dependency = ReadDependencies().SingleOrDefault(d => d.Name == assemblyName); + if (dependency == null) + return null; + + if (!_virtualPathProvider.FileExists(_virtualPathProvider.Combine(BasePath, dependency.FileName))) + return null; + + return Assembly.Load(Path.GetFileNameWithoutExtension(dependency.FileName)); + } + + private class DependencyDescritpor { + public string Name { get; set; } + public string FileName { get; set; } + } + + private IEnumerable ReadDependencies() { + if (!_virtualPathProvider.FileExists(PersistencePath)) + return Enumerable.Empty(); + + using (var stream = _virtualPathProvider.OpenFile(PersistencePath)) { + XDocument document = XDocument.Load(stream); + return document + .Elements(ns("Dependencies")) + .Elements(ns("Dependency")) + .Select(e => new DependencyDescritpor { Name = e.Element("Name").Value, FileName = e.Element("FileName").Value }) + .ToList(); + } + } + + private void WriteDependencies(IEnumerable dependencies) { + var document = new XDocument(); + document.Add(new XElement(ns("Dependencies"))); + var elements = dependencies.Select(d => new XElement("Dependency", + new XElement(ns("Name"), d.Name), + new XElement(ns("FileName"), d.FileName))); + document.Root.Add(elements); + + using (var stream = _virtualPathProvider.CreateText(PersistencePath)) { + document.Save(stream, SaveOptions.None); + } + } + + private static XName ns(string name) { + return XName.Get(name/*, "http://schemas.microsoft.com/developer/msbuild/2003"*/); + } + } +} diff --git a/src/Orchard/Orchard.Framework.csproj b/src/Orchard/Orchard.Framework.csproj index 1712e764a..69112e90b 100644 --- a/src/Orchard/Orchard.Framework.csproj +++ b/src/Orchard/Orchard.Framework.csproj @@ -136,14 +136,17 @@ - - - - - - - - + + + + + + + + + + +