mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-09-19 10:07:55 +08:00
Support for dynamic loading of module references
--HG-- branch : dev
This commit is contained in:
@@ -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) } };
|
||||
}
|
||||
|
@@ -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) } };
|
||||
}
|
||||
|
@@ -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,20 +40,33 @@ 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) {
|
||||
catch (Exception e) {
|
||||
throw new OrchardCoreException(T("Error compiling module \"{0}\" from file \"{1}\"", moduleName, context.VirtualPath), e);
|
||||
}
|
||||
}
|
||||
|
@@ -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) {
|
||||
|
@@ -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) {
|
||||
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))
|
||||
.GroupBy(e => e.Descriptor.Name)
|
||||
.ToDictionary(g => g.Key, g => g.AsEnumerable()
|
||||
.OrderByDescending(probe => probe.LastModificationTimeUtc)
|
||||
.ThenBy(probe => probe.Loader.Order), StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
var extensionProbes = _loaders
|
||||
.Select(loader => loader.Probe(extension))
|
||||
.Where(probe => probe != null)
|
||||
.OrderByDescending(probe => probe.LastModificationTimeUtc)
|
||||
.ThenBy(probe => probe.Loader.Order)
|
||||
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
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -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; }
|
||||
}
|
||||
}
|
@@ -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)
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
|
||||
|
@@ -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)
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@@ -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) ^
|
||||
|
@@ -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 {
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user