Support for dynamic loading of module references

--HG--
branch : dev
This commit is contained in:
Renaud Paquay
2010-06-30 18:18:11 -07:00
parent 21328875f3
commit c43e87662c
15 changed files with 430 additions and 45 deletions

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Autofac;
using NUnit.Framework;
using Orchard.Caching;
@@ -56,10 +57,26 @@ namespace Orchard.Tests.Environment.Extensions {
get { return this.GetType().Name; }
}
public Assembly LoadReference(ReferenceDescriptor reference) {
throw new NotImplementedException();
}
public void ReferenceActivated(ExtensionLoadingContext context, ExtensionReferenceEntry referenceEntry) {
throw new NotImplementedException();
}
public void ReferenceDeactivated(ExtensionLoadingContext context, ExtensionReferenceEntry referenceEntry) {
throw new NotImplementedException();
}
public ExtensionProbeEntry Probe(ExtensionDescriptor descriptor) {
return new ExtensionProbeEntry { Descriptor = descriptor, Loader = this };
}
public IEnumerable<ExtensionReferenceEntry> ProbeReferences(ExtensionDescriptor extensionDescriptor) {
throw new NotImplementedException();
}
public ExtensionEntry Load(ExtensionDescriptor descriptor) {
return new ExtensionEntry { Descriptor = descriptor, ExportedTypes = new[] { typeof(Alpha), typeof(Beta), typeof(Phi) } };
}

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Autofac;
using NUnit.Framework;
using Orchard.Caching;
@@ -55,10 +56,26 @@ namespace Orchard.Tests.Environment.Extensions {
get { throw new NotImplementedException(); }
}
public Assembly LoadReference(ReferenceDescriptor reference) {
throw new NotImplementedException();
}
public void ReferenceActivated(ExtensionLoadingContext context, ExtensionReferenceEntry referenceEntry) {
throw new NotImplementedException();
}
public void ReferenceDeactivated(ExtensionLoadingContext context, ExtensionReferenceEntry referenceEntry) {
throw new NotImplementedException();
}
public ExtensionProbeEntry Probe(ExtensionDescriptor descriptor) {
return new ExtensionProbeEntry { Descriptor = descriptor, Loader = this };
}
public IEnumerable<ExtensionReferenceEntry> ProbeReferences(ExtensionDescriptor extensionDescriptor) {
throw new NotImplementedException();
}
public ExtensionEntry Load(ExtensionDescriptor descriptor) {
return new ExtensionEntry { Descriptor = descriptor, ExportedTypes = new[] { typeof(Alpha), typeof(Beta), typeof(Phi) } };
}

View File

@@ -1,7 +1,9 @@
using System;
using System.CodeDom;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Orchard.Environment.Extensions.Loaders;
using Orchard.FileSystems.Dependencies;
using Orchard.FileSystems.VirtualPath;
using Orchard.Localization;
@@ -9,17 +11,24 @@ using Orchard.Logging;
namespace Orchard.Environment.Extensions.Compilers {
/// <summary>
/// Compile a C# extension into an assembly given a directory location
/// Compile an extension project file into an assembly
/// </summary>
public class DefaultExtensionCompiler : IExtensionCompiler {
private readonly IVirtualPathProvider _virtualPathProvider;
private readonly IProjectFileParser _projectFileParser;
private readonly IDependenciesFolder _dependenciesFolder;
private readonly IEnumerable<IExtensionLoader> _loaders;
public DefaultExtensionCompiler(
IVirtualPathProvider virtualPathProvider,
IProjectFileParser projectFileParser,
IDependenciesFolder dependenciesFolder,
IEnumerable<IExtensionLoader> loaders) {
public DefaultExtensionCompiler(IVirtualPathProvider virtualPathProvider, IProjectFileParser projectFileParser, IDependenciesFolder dependenciesFolder ) {
_virtualPathProvider = virtualPathProvider;
_projectFileParser = projectFileParser;
_dependenciesFolder = dependenciesFolder;
_loaders = loaders;
T = NullLocalizer.Instance;
Logger = NullLogger.Instance;
@@ -31,17 +40,30 @@ namespace Orchard.Environment.Extensions.Compilers {
public void Compile(CompileExtensionContext context) {
Logger.Information("Generate code for file \"{0}\"", context.VirtualPath);
var moduleName = Path.GetFileNameWithoutExtension(context.VirtualPath);
if (_dependenciesFolder.GetDescriptor(moduleName) == null)
var dependencyDescriptor = _dependenciesFolder.GetDescriptor(moduleName);
if (dependencyDescriptor == null)
return;
try {
using (var stream = _virtualPathProvider.OpenFile(context.VirtualPath)) {
var descriptor = _projectFileParser.Parse(stream);
// Add source files
var directory = _virtualPathProvider.GetDirectoryName(context.VirtualPath);
foreach (var filename in descriptor.SourceFilenames.Select(f => _virtualPathProvider.Combine(directory, f))) {
context.AssemblyBuilder.AddCodeCompileUnit(CreateCompileUnit(filename));
}
// Add assembly references
foreach (var reference in dependencyDescriptor.References) {
var referenceTemp = reference;
var loader = _loaders.SingleOrDefault(l => l.Name == referenceTemp.LoaderName);
if (loader != null) {
var assembly = loader.LoadReference(reference);
if (assembly != null)
context.AssemblyBuilder.AddAssemblyReference(assembly);
}
}
}
}
catch (Exception e) {

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml;
@@ -40,7 +41,14 @@ namespace Orchard.Environment.Extensions.Compilers {
.Elements(ns("ItemGroup"))
.Elements(ns("Reference"))
.Attributes("Include")
.Select(c => new ReferenceDescriptor { AssemblyName = c.Value });
.Select(c => new ReferenceDescriptor { AssemblyName = ExtractAssemblyName(c.Value) });
}
private static string ExtractAssemblyName(string value) {
int index = value.IndexOf(',');
if (index < 0)
return value;
return value.Substring(0, index);
}
private static XName ns(string name) {

View File

@@ -5,6 +5,7 @@ using Orchard.Caching;
using Orchard.Environment.Extensions.Loaders;
using Orchard.Environment.Extensions.Models;
using Orchard.FileSystems.Dependencies;
using Orchard.FileSystems.VirtualPath;
using Orchard.Localization;
using Orchard.Logging;
@@ -12,19 +13,25 @@ namespace Orchard.Environment.Extensions {
public class ExtensionLoaderCoordinator : IExtensionLoaderCoordinator {
private readonly IDependenciesFolder _dependenciesFolder;
private readonly IExtensionManager _extensionManager;
private readonly IVirtualPathProvider _virtualPathProvider;
private readonly IEnumerable<IExtensionLoader> _loaders;
private readonly IHostEnvironment _hostEnvironment;
private readonly IBuildManager _buildManager;
public ExtensionLoaderCoordinator(
IDependenciesFolder dependenciesFolder,
IExtensionManager extensionManager,
IVirtualPathProvider virtualPathProvider,
IEnumerable<IExtensionLoader> loaders,
IHostEnvironment hostEnvironment) {
IHostEnvironment hostEnvironment,
IBuildManager buildManager) {
_dependenciesFolder = dependenciesFolder;
_extensionManager = extensionManager;
_virtualPathProvider = virtualPathProvider;
_loaders = loaders.OrderBy(l => l.Order);
_hostEnvironment = hostEnvironment;
_buildManager = buildManager;
T = NullLocalizer.Instance;
Logger = NullLogger.Instance;
@@ -36,50 +43,76 @@ namespace Orchard.Environment.Extensions {
public void SetupExtensions() {
Logger.Information("Loading extensions.");
var extensions = _extensionManager.AvailableExtensions().Where(d => d.ExtensionType == "Module").ToList();
var existingDependencies = _dependenciesFolder.LoadDescriptors().ToList();
var deletedDependencies = existingDependencies.Where(e => !extensions.Any(e2 => StringComparer.OrdinalIgnoreCase.Equals(e2.Name, e.Name))).ToList();
var loadingContext = new ExtensionLoadingContext();
var context = CreateLoadingContext();
// Notify all loaders about extensions removed from the web site
foreach (var dependency in deletedDependencies) {
foreach (var dependency in context.DeletedDependencies) {
Logger.Information("Extension {0} has been removed from site", dependency.Name);
foreach (var loader in _loaders) {
if (dependency.LoaderName == loader.Name) {
loader.ExtensionRemoved(loadingContext, dependency);
loader.ExtensionRemoved(context, dependency);
}
}
}
// For all existing extensions in the site, ask each loader if they can
// load that extension.
var newDependencies = new List<DependencyDescriptor>();
foreach (var extension in extensions) {
ProcessExtension(loadingContext, extension, existingDependencies, newDependencies);
foreach (var extension in context.AvailableExtensions) {
ProcessExtension(context, extension);
}
// Execute all the work need by "ctx"
ProcessContextCommands(loadingContext);
ProcessContextCommands(context);
// And finally save the new entries in the dependencies folder
_dependenciesFolder.StoreDescriptors(newDependencies);
_dependenciesFolder.StoreDescriptors(context.NewDependencies);
Logger.Information("Done loading extensions.");
}
private void ProcessExtension(
ExtensionLoadingContext loadingContext,
ExtensionDescriptor extension,
IEnumerable<DependencyDescriptor> existingDependencies,
List<DependencyDescriptor> newDependencies) {
var extensionProbes = _loaders
private ExtensionLoadingContext CreateLoadingContext() {
var availableExtensions = _extensionManager.AvailableExtensions().Where(d => d.ExtensionType == "Module").ToList();
var previousDependencies = _dependenciesFolder.LoadDescriptors().ToList();
var availableExtensionsProbes = availableExtensions.SelectMany(extension => _loaders
.Select(loader => loader.Probe(extension))
.Where(probe => probe != null)
.Where(probe => probe != null))
.GroupBy(e => e.Descriptor.Name)
.ToDictionary(g => g.Key, g => g.AsEnumerable()
.OrderByDescending(probe => probe.LastModificationTimeUtc)
.ThenBy(probe => probe.Loader.Order)
.ThenBy(probe => probe.Loader.Order), StringComparer.OrdinalIgnoreCase);
var deletedDependencies = previousDependencies
.Where(e => !availableExtensions.Any(e2 => StringComparer.OrdinalIgnoreCase.Equals(e2.Name, e.Name)))
.ToList();
// Collect references for all modules
var references =
availableExtensions
.SelectMany(extension => _loaders.SelectMany(loader => loader.ProbeReferences(extension)))
.ToList();
var referencesByModule = references
.GroupBy(entry => entry.Descriptor.Name, StringComparer.OrdinalIgnoreCase)
.ToDictionary(g => g.Key, g => g.AsEnumerable(), StringComparer.OrdinalIgnoreCase);
var referencesByName = references
.GroupBy(reference => reference.Name, StringComparer.OrdinalIgnoreCase)
.ToDictionary(g => g.Key, g => g.AsEnumerable(), StringComparer.OrdinalIgnoreCase);
return new ExtensionLoadingContext {
AvailableExtensions = availableExtensions,
PreviousDependencies = previousDependencies,
DeletedDependencies = deletedDependencies,
AvailableExtensionsProbes = availableExtensionsProbes,
ReferencesByName = referencesByName,
ReferencesByModule = referencesByModule
};
}
private void ProcessExtension(ExtensionLoadingContext context, ExtensionDescriptor extension) {
var extensionProbes = context.AvailableExtensionsProbes.ContainsKey(extension.Name) ?
context.AvailableExtensionsProbes[extension.Name] :
Enumerable.Empty<ExtensionProbeEntry>();
if (Logger.IsEnabled(LogLevel.Debug)) {
Logger.Debug("Loaders for extension \"{0}\": ", extension.Name);
@@ -91,28 +124,110 @@ namespace Orchard.Environment.Extensions {
}
var activatedExtension = extensionProbes.FirstOrDefault();
var previousDependency = existingDependencies.Where(d => StringComparer.OrdinalIgnoreCase.Equals(d.Name, extension.Name)).FirstOrDefault();
var previousDependency = context.PreviousDependencies.Where(d => StringComparer.OrdinalIgnoreCase.Equals(d.Name, extension.Name)).FirstOrDefault();
if (activatedExtension == null) {
Logger.Warning("No loader found for extension \"{0}\"!", extension.Name);
}
var references = ProcessExtensionReferences(context, activatedExtension);
foreach (var loader in _loaders) {
if (activatedExtension != null && activatedExtension.Loader.Name == loader.Name) {
Logger.Information("Activating extension \"{0}\" with loader \"{1}\"", activatedExtension.Descriptor.Name, loader.Name);
loader.ExtensionActivated(loadingContext, extension);
loader.ExtensionActivated(context, extension);
}
else if (previousDependency != null && previousDependency.LoaderName == loader.Name) {
Logger.Information("Deactivating extension \"{0}\" from loader \"{1}\"", previousDependency.Name, loader.Name);
loader.ExtensionDeactivated(loadingContext, extension);
loader.ExtensionDeactivated(context, extension);
}
}
if (activatedExtension != null) {
newDependencies.Add(new DependencyDescriptor {
context.NewDependencies.Add(new DependencyDescriptor {
Name = extension.Name,
LoaderName = activatedExtension.Loader.Name,
VirtualPath = activatedExtension.VirtualPath
VirtualPath = activatedExtension.VirtualPath,
References = references
});
}
}
IEnumerable<ReferenceDescriptor> ProcessExtensionReferences(ExtensionLoadingContext context, ExtensionProbeEntry activatedExtension) {
if (activatedExtension == null)
return Enumerable.Empty<ReferenceDescriptor>();
var referenceNames = (context.ReferencesByModule.ContainsKey(activatedExtension.Descriptor.Name) ?
context.ReferencesByModule[activatedExtension.Descriptor.Name] :
Enumerable.Empty<ExtensionReferenceEntry>())
.Select(r => r.Name)
.Distinct(StringComparer.OrdinalIgnoreCase);
var referencesDecriptors = new List<ReferenceDescriptor>();
foreach (var referenceName in referenceNames) {
ProcessExtensionReference(context, activatedExtension, referenceName, referencesDecriptors);
}
return referencesDecriptors;
}
private void ProcessExtensionReference(ExtensionLoadingContext context,
ExtensionProbeEntry activatedExtension,
string referenceName,
IList<ReferenceDescriptor> activatedReferences) {
// Assemblies loaded by the BuildManager are ignored, since
// we don't want to update them and they are automatically
// referenced by the build manager
if (_buildManager.GetReferencedAssemblies().Any(a => StringComparer.OrdinalIgnoreCase.Equals(a.GetName().Name, referenceName)))
return;
var references = context.ReferencesByName.ContainsKey(referenceName) ?
context.ReferencesByName[referenceName] :
Enumerable.Empty<ExtensionReferenceEntry>();
// Binary references
var bestBinaryReference = references
.Where(entry => !string.IsNullOrEmpty(entry.VirtualPath))
.Select(entry => new { Entry = entry, LastWriteTimeUtc = _virtualPathProvider.GetFileLastWriteTimeUtc(entry.VirtualPath) })
.OrderBy(e => e.LastWriteTimeUtc)
.ThenBy(e => e.Entry.Name).FirstOrDefault();
var probes = context.AvailableExtensionsProbes.ContainsKey(referenceName) ?
context.AvailableExtensionsProbes[referenceName] :
Enumerable.Empty<ExtensionProbeEntry>();
var bestProbe = probes.FirstOrDefault();
// Pick the best one of module vs binary
if (bestProbe != null && bestBinaryReference != null) {
if (bestProbe.LastModificationTimeUtc >= bestBinaryReference.LastWriteTimeUtc) {
bestBinaryReference = null;
} else {
bestProbe = null;
}
}
// Activate the binary ref
if (bestBinaryReference != null) {
if (!context.ProcessedReferences.Contains(bestBinaryReference.Entry.Name)) {
context.ProcessedReferences.Add(bestBinaryReference.Entry.Name);
bestBinaryReference.Entry.Loader.ReferenceActivated(context, bestBinaryReference.Entry);
}
activatedReferences.Add(new ReferenceDescriptor {
LoaderName = bestBinaryReference.Entry.Loader.Name,
Name = bestBinaryReference.Entry.Name,
VirtualPath = bestBinaryReference.Entry.VirtualPath
});
return;
}
// Activated the module ref
if (bestProbe != null)
{
activatedReferences.Add(new ReferenceDescriptor {
LoaderName = bestProbe.Loader.Name,
Name = referenceName,
VirtualPath = bestProbe.VirtualPath
});
}
}

View File

@@ -1,17 +1,61 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Orchard.Environment.Extensions.Loaders;
using Orchard.Environment.Extensions.Models;
using Orchard.FileSystems.Dependencies;
namespace Orchard.Environment.Extensions {
public class ExtensionLoadingContext {
public ExtensionLoadingContext() {
ProcessedExtensions = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
ProcessedReferences = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
DeleteActions = new List<Action>();
CopyActions = new List<Action>();
NewDependencies = new List<DependencyDescriptor>();
}
public IList<Action> DeleteActions { get; set; }
public IList<Action> CopyActions { get; set; }
public ISet<string> ProcessedExtensions { get; private set; }
public ISet<string> ProcessedReferences { get; private set; }
public IList<DependencyDescriptor> NewDependencies { get; private set; }
public IList<Action> DeleteActions { get; private set; }
public IList<Action> CopyActions { get; private set; }
public bool RestartAppDomain { get; set; }
public bool ResetSiteCompilation { get; set; }
/// <summary>
/// List of extensions (modules) present in the system
/// </summary>
public List<ExtensionDescriptor> AvailableExtensions { get; set; }
/// <summary>
/// List of extensions (modules) that were loaded during a previous successful run
/// </summary>
public List<DependencyDescriptor> PreviousDependencies { get; set; }
/// <summary>
/// The list of extensions/modules that are were present in the previous successful run
/// and that are not present in the system anymore.
/// </summary>
public List<DependencyDescriptor> DeletedDependencies { get; set; }
/// <summary>
/// For every extension name, the list of loaders that can potentially load
/// that extension (in order of "best-of" applicable)
/// </summary>
public IDictionary<string, IOrderedEnumerable<ExtensionProbeEntry>> AvailableExtensionsProbes { get; set; }
/// <summary>
/// For every reference name, list of potential loaders/locations
/// </summary>
public IDictionary<string, IEnumerable<ExtensionReferenceEntry>> ReferencesByModule { get; set; }
/// <summary>
/// For every extension name, list of references
/// </summary>
public IDictionary<string, IEnumerable<ExtensionReferenceEntry>> ReferencesByName { get; set; }
}
}

View File

@@ -2,29 +2,35 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using Orchard.Caching;
using Orchard.Environment.Extensions.Compilers;
using Orchard.Environment.Extensions.Models;
using Orchard.FileSystems.Dependencies;
using Orchard.FileSystems.VirtualPath;
using Orchard.Logging;
using ReferenceDescriptor = Orchard.FileSystems.Dependencies.ReferenceDescriptor;
namespace Orchard.Environment.Extensions.Loaders {
public class DynamicExtensionLoader : ExtensionLoaderBase {
private readonly IBuildManager _buildManager;
private readonly IVirtualPathProvider _virtualPathProvider;
private readonly IVirtualPathMonitor _virtualPathMonitor;
private readonly IProjectFileParser _projectFileParser;
private static readonly ReloadWorkaround _reloadWorkaround = new ReloadWorkaround();
public DynamicExtensionLoader(
IBuildManager buildManager,
IVirtualPathProvider virtualPathProvider,
IVirtualPathMonitor virtualPathMonitor,
IDependenciesFolder dependenciesFolder)
IDependenciesFolder dependenciesFolder,
IProjectFileParser projectFileParser)
: base(dependenciesFolder) {
_buildManager = buildManager;
_virtualPathProvider = virtualPathProvider;
_virtualPathMonitor = virtualPathMonitor;
_projectFileParser = projectFileParser;
Logger = NullLogger.Instance;
}
@@ -74,6 +80,27 @@ namespace Orchard.Environment.Extensions.Loaders {
}
}
public override IEnumerable<ExtensionReferenceEntry> ProbeReferences(ExtensionDescriptor descriptor) {
string projectPath = GetProjectPath(descriptor);
if (projectPath == null)
return Enumerable.Empty<ExtensionReferenceEntry>();
using(var stream = _virtualPathProvider.OpenFile(projectPath)) {
var projectFile = _projectFileParser.Parse(stream);
return projectFile.References.Select(r => new ExtensionReferenceEntry {
Descriptor = descriptor,
Loader = this,
Name = r.AssemblyName,
VirtualPath = null
});
}
}
public override Assembly LoadReference(ReferenceDescriptor reference) {
return _buildManager.GetCompiledAssembly(reference.VirtualPath);
}
public override ExtensionProbeEntry Probe(ExtensionDescriptor descriptor) {
string projectPath = GetProjectPath(descriptor);
if (projectPath == null)

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Orchard.Caching;
using Orchard.Environment.Extensions.Models;
using Orchard.FileSystems.Dependencies;
@@ -16,6 +17,14 @@ namespace Orchard.Environment.Extensions.Loaders {
public abstract int Order { get; }
public string Name { get { return this.GetType().Name; } }
public virtual IEnumerable<ExtensionReferenceEntry> ProbeReferences(ExtensionDescriptor descriptor) {
return Enumerable.Empty<ExtensionReferenceEntry>();
}
public virtual Assembly LoadReference(ReferenceDescriptor reference) {
return null;
}
public abstract ExtensionProbeEntry Probe(ExtensionDescriptor descriptor);
public ExtensionEntry Load(ExtensionDescriptor descriptor) {
@@ -26,9 +35,13 @@ namespace Orchard.Environment.Extensions.Loaders {
return null;
}
public virtual void ReferenceActivated(ExtensionLoadingContext context, ExtensionReferenceEntry referenceEntry) { }
public virtual void ReferenceDeactivated(ExtensionLoadingContext context, ExtensionReferenceEntry referenceEntry) { }
public virtual void ExtensionActivated(ExtensionLoadingContext ctx, ExtensionDescriptor extension) { }
public virtual void ExtensionDeactivated(ExtensionLoadingContext ctx, ExtensionDescriptor extension) { }
public virtual void ExtensionRemoved(ExtensionLoadingContext ctx, DependencyDescriptor dependency) { }
public virtual void Monitor(ExtensionDescriptor extension, Action<IVolatileToken> monitor) { }
protected abstract ExtensionEntry LoadWorker(ExtensionDescriptor descriptor);

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using Orchard.Caching;
using Orchard.Environment.Extensions.Models;
using Orchard.FileSystems.Dependencies;
@@ -12,10 +13,22 @@ namespace Orchard.Environment.Extensions.Loaders {
public DateTime LastModificationTimeUtc { get; set; }
}
public class ExtensionReferenceEntry {
public ExtensionDescriptor Descriptor { get; set; }
public IExtensionLoader Loader { get; set; }
public string Name { get; set; }
public string VirtualPath { get; set; }
}
public interface IExtensionLoader {
int Order { get; }
string Name { get; }
IEnumerable<ExtensionReferenceEntry> ProbeReferences(ExtensionDescriptor extensionDescriptor);
Assembly LoadReference(ReferenceDescriptor reference);
void ReferenceActivated(ExtensionLoadingContext context, ExtensionReferenceEntry referenceEntry);
void ReferenceDeactivated(ExtensionLoadingContext context, ExtensionReferenceEntry referenceEntry);
ExtensionProbeEntry Probe(ExtensionDescriptor descriptor);
ExtensionEntry Load(ExtensionDescriptor descriptor);

View File

@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using Orchard.Caching;
using Orchard.Environment.Extensions.Models;
using Orchard.FileSystems.Dependencies;
@@ -93,6 +95,28 @@ namespace Orchard.Environment.Extensions.Loaders {
}
}
public override void ReferenceActivated(ExtensionLoadingContext context, ExtensionReferenceEntry referenceEntry) {
if (string.IsNullOrEmpty(referenceEntry.VirtualPath))
return;
string sourceFileName = _virtualPathProvider.MapPath(referenceEntry.VirtualPath);
// Copy the assembly if it doesn't exist or if it is older than the source file.
bool copyAssembly =
!_assemblyProbingFolder.AssemblyExists(referenceEntry.Name) ||
File.GetLastWriteTimeUtc(sourceFileName) > _assemblyProbingFolder.GetAssemblyDateTimeUtc(referenceEntry.Name);
if (copyAssembly) {
context.CopyActions.Add(() => _assemblyProbingFolder.StoreAssembly(referenceEntry.Name, sourceFileName));
// We need to restart the appDomain if the assembly is loaded
if (IsAssemblyLoaded(referenceEntry.Name)) {
Logger.Information("ReferenceActivated: Reference \"{0}\" is activated with newer file and its assembly is loaded, forcing AppDomain restart", referenceEntry.Name);
context.RestartAppDomain = true;
}
}
}
public override void Monitor(ExtensionDescriptor descriptor, Action<IVolatileToken> monitor) {
string assemblyPath = GetAssemblyPath(descriptor);
if (assemblyPath != null) {
@@ -101,6 +125,23 @@ namespace Orchard.Environment.Extensions.Loaders {
}
}
public override IEnumerable<ExtensionReferenceEntry> ProbeReferences(ExtensionDescriptor descriptor) {
var assemblyPath = GetAssemblyPath(descriptor);
if (assemblyPath == null)
return Enumerable.Empty<ExtensionReferenceEntry>();
return _virtualPathProvider
.ListFiles(_virtualPathProvider.GetDirectoryName(assemblyPath))
.Where(s => StringComparer.OrdinalIgnoreCase.Equals(Path.GetExtension(s), ".dll"))
.Where(s => !StringComparer.OrdinalIgnoreCase.Equals(Path.GetFileNameWithoutExtension(s), descriptor.Name))
.Select(path => new ExtensionReferenceEntry {
Descriptor = descriptor,
Loader = this,
Name = Path.GetFileNameWithoutExtension(path),
VirtualPath = path
} );
}
public override ExtensionProbeEntry Probe(ExtensionDescriptor descriptor) {
var assemblyPath = GetAssemblyPath(descriptor);
if (assemblyPath == null)
@@ -114,6 +155,10 @@ namespace Orchard.Environment.Extensions.Loaders {
};
}
public override Assembly LoadReference(ReferenceDescriptor reference) {
return _assemblyProbingFolder.LoadAssembly(reference.Name);
}
protected override ExtensionEntry LoadWorker(ExtensionDescriptor descriptor) {
var assembly = _assemblyProbingFolder.LoadAssembly(descriptor.Name);
if (assembly == null)

View File

@@ -1,9 +1,12 @@
using System.CodeDom;
using System;
using System.CodeDom;
using System.Reflection;
using System.Web.Compilation;
namespace Orchard.Environment {
public interface IAssemblyBuilder {
void AddCodeCompileUnit(CodeCompileUnit compileUnit);
void AddAssemblyReference(Assembly assembly);
}
public class AspNetAssemblyBuilder : IAssemblyBuilder {
@@ -18,5 +21,9 @@ namespace Orchard.Environment {
public void AddCodeCompileUnit(CodeCompileUnit compileUnit) {
_assemblyBuilder.AddCodeCompileUnit(_buildProvider, compileUnit);
}
public void AddAssemblyReference(Assembly assembly) {
_assemblyBuilder.AddAssemblyReference(assembly);
}
}
}

View File

@@ -69,9 +69,12 @@ namespace Orchard.FileSystems.Dependencies {
.Select(e => new DependencyDescriptor {
Name = elem(e, "ModuleName"),
VirtualPath = elem(e, "VirtualPath"),
LoaderName = elem(e, "LoaderName")
})
.ToList();
LoaderName = elem(e, "LoaderName"),
References = e.Elements(ns("References")).Elements(ns("Reference")).Select(r => new ReferenceDescriptor {
Name = elem(r, "Name"),
LoaderName = elem(r, "LoaderName"),
VirtualPath = elem(r, "VirtualPath")
})}).ToList();
}
}
@@ -83,7 +86,13 @@ namespace Orchard.FileSystems.Dependencies {
var elements = dependencies.Select(d => new XElement("Dependency",
new XElement(ns("ModuleName"), d.Name),
new XElement(ns("VirtualPath"), d.VirtualPath),
new XElement(ns("LoaderName"), d.LoaderName)));
new XElement(ns("LoaderName"), d.LoaderName),
new XElement(ns("References"), d.References
.Select(r => new XElement(ns("Reference"),
new XElement(ns("Name"), r.Name),
new XElement(ns("LoaderName"), r.LoaderName),
new XElement(ns("VirtualPath"), r.VirtualPath))).ToArray())));
document.Root.Add(elements);
using (var stream = _appDataFolder.CreateFile(persistancePath)) {
@@ -99,7 +108,27 @@ namespace Orchard.FileSystems.Dependencies {
}
private class DependencyDescriptorComparer : EqualityComparer<DependencyDescriptor> {
private readonly ReferenceDescriptorComparer _referenceDescriptorComparer = new ReferenceDescriptorComparer();
public override bool Equals(DependencyDescriptor x, DependencyDescriptor y) {
return
StringComparer.OrdinalIgnoreCase.Equals(x.Name, y.Name) &&
StringComparer.OrdinalIgnoreCase.Equals(x.LoaderName, y.LoaderName) &&
StringComparer.OrdinalIgnoreCase.Equals(x.VirtualPath, y.VirtualPath) &&
x.References.SequenceEqual(y.References, _referenceDescriptorComparer);
}
public override int GetHashCode(DependencyDescriptor obj) {
return
StringComparer.OrdinalIgnoreCase.GetHashCode(obj.Name) ^
StringComparer.OrdinalIgnoreCase.GetHashCode(obj.LoaderName) ^
StringComparer.OrdinalIgnoreCase.GetHashCode(obj.VirtualPath) ^
obj.References.Aggregate(0, (a, entry) => a + _referenceDescriptorComparer.GetHashCode(entry));
}
}
private class ReferenceDescriptorComparer : EqualityComparer<ReferenceDescriptor> {
public override bool Equals(ReferenceDescriptor x, ReferenceDescriptor y) {
return
StringComparer.OrdinalIgnoreCase.Equals(x.Name, y.Name) &&
StringComparer.OrdinalIgnoreCase.Equals(x.LoaderName, y.LoaderName) &&
@@ -107,7 +136,7 @@ namespace Orchard.FileSystems.Dependencies {
}
public override int GetHashCode(DependencyDescriptor obj) {
public override int GetHashCode(ReferenceDescriptor obj) {
return
StringComparer.OrdinalIgnoreCase.GetHashCode(obj.Name) ^
StringComparer.OrdinalIgnoreCase.GetHashCode(obj.LoaderName) ^

View File

@@ -6,6 +6,13 @@ namespace Orchard.FileSystems.Dependencies {
public string Name { get; set; }
public string LoaderName { get; set; }
public string VirtualPath { get; set; }
public IEnumerable<ReferenceDescriptor> References { get; set; }
}
public class ReferenceDescriptor {
public string Name { get; set; }
public string LoaderName { get; set; }
public string VirtualPath { get; set; }
}
public interface IDependenciesFolder : IVolatileProvider {

View File

@@ -1,4 +1,7 @@
using System.IO;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web.Hosting;
namespace Orchard.FileSystems.VirtualPath {
@@ -7,6 +10,14 @@ namespace Orchard.FileSystems.VirtualPath {
return Path.GetDirectoryName(virtualPath).Replace(Path.DirectorySeparatorChar, '/');
}
public IEnumerable<string> ListFiles(string path) {
return HostingEnvironment.VirtualPathProvider.GetDirectory(path).Files.OfType<VirtualFile>().Select(f => f.VirtualPath);
}
public IEnumerable<string> ListDirectories(string path) {
return HostingEnvironment.VirtualPathProvider.GetDirectory(path).Directories.OfType<VirtualDirectory>().Select(d => d.VirtualPath);
}
public string Combine(params string[] paths) {
return Path.Combine(paths).Replace(Path.DirectorySeparatorChar, '/');
}
@@ -19,6 +30,10 @@ namespace Orchard.FileSystems.VirtualPath {
return File.CreateText(MapPath(virtualPath));
}
public DateTime GetFileLastWriteTimeUtc(string virtualPath) {
return File.GetLastWriteTimeUtc(MapPath(virtualPath));
}
public string MapPath(string virtualPath) {
return HostingEnvironment.MapPath(virtualPath);
}

View File

@@ -1,4 +1,6 @@
using System.IO;
using System;
using System.Collections.Generic;
using System.IO;
using Orchard.Caching;
namespace Orchard.FileSystems.VirtualPath {
@@ -9,9 +11,13 @@ namespace Orchard.FileSystems.VirtualPath {
bool FileExists(string virtualPath);
Stream OpenFile(string virtualPath);
StreamWriter CreateText(string virtualPath);
DateTime GetFileLastWriteTimeUtc(string virtualPath);
bool DirectoryExists(string virtualPath);
void CreateDirectory(string virtualPath);
string GetDirectoryName(string virtualPath);
IEnumerable<string> ListFiles(string path);
IEnumerable<string> ListDirectories(string path);
}
}