diff --git a/src/Orchard.Tests/Environment/Extensions/ExtensionLoaderCoordinatorTests.cs b/src/Orchard.Tests/Environment/Extensions/ExtensionLoaderCoordinatorTests.cs index 168cb1a39..294dae0bf 100644 --- a/src/Orchard.Tests/Environment/Extensions/ExtensionLoaderCoordinatorTests.cs +++ b/src/Orchard.Tests/Environment/Extensions/ExtensionLoaderCoordinatorTests.cs @@ -69,6 +69,10 @@ namespace Orchard.Tests.Environment.Extensions { throw new NotImplementedException(); } + public bool IsCompatibleWithReferences(ExtensionDescriptor extension, IEnumerable references) { + throw new NotImplementedException(); + } + public ExtensionProbeEntry Probe(ExtensionDescriptor descriptor) { return new ExtensionProbeEntry { Descriptor = descriptor, Loader = this }; } diff --git a/src/Orchard.Tests/Environment/Extensions/ExtensionManagerTests.cs b/src/Orchard.Tests/Environment/Extensions/ExtensionManagerTests.cs index 46bd59bc8..41ef9dd98 100644 --- a/src/Orchard.Tests/Environment/Extensions/ExtensionManagerTests.cs +++ b/src/Orchard.Tests/Environment/Extensions/ExtensionManagerTests.cs @@ -68,6 +68,10 @@ namespace Orchard.Tests.Environment.Extensions { throw new NotImplementedException(); } + public bool IsCompatibleWithReferences(ExtensionDescriptor extension, IEnumerable references) { + throw new NotImplementedException(); + } + public ExtensionProbeEntry Probe(ExtensionDescriptor descriptor) { return new ExtensionProbeEntry { Descriptor = descriptor, Loader = this }; } diff --git a/src/Orchard/Environment/Extensions/ExtensionLoaderCoordinator.cs b/src/Orchard/Environment/Extensions/ExtensionLoaderCoordinator.cs index 11dbb4fa3..6b581f649 100644 --- a/src/Orchard/Environment/Extensions/ExtensionLoaderCoordinator.cs +++ b/src/Orchard/Environment/Extensions/ExtensionLoaderCoordinator.cs @@ -8,6 +8,7 @@ using Orchard.FileSystems.Dependencies; using Orchard.FileSystems.VirtualPath; using Orchard.Localization; using Orchard.Logging; +using Orchard.Utility; namespace Orchard.Environment.Extensions { public class ExtensionLoaderCoordinator : IExtensionLoaderCoordinator { @@ -69,45 +70,6 @@ namespace Orchard.Environment.Extensions { Logger.Information("Done loading extensions."); } - 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 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) ? @@ -123,7 +85,21 @@ namespace Orchard.Environment.Extensions { } } - var activatedExtension = extensionProbes.FirstOrDefault(); + var moduleReferences = + context.AvailableExtensions + .Where(e => + context.ReferencesByModule.ContainsKey(extension.Name) && + context.ReferencesByModule[extension.Name].Any(r => StringComparer.OrdinalIgnoreCase.Equals(e.Name, r.Name))); + + var processedModuleReferences = + moduleReferences + .Where(e => context.ProcessedExtensions.ContainsKey(e.Name)) + .Select(e => context.ProcessedExtensions[e.Name]); + + var activatedExtension = extensionProbes + .Where(e => e.Loader.IsCompatibleWithReferences(extension, processedModuleReferences)) + .FirstOrDefault(); + var previousDependency = context.PreviousDependencies.Where(d => StringComparer.OrdinalIgnoreCase.Equals(d.Name, extension.Name)).FirstOrDefault(); if (activatedExtension == null) { @@ -151,6 +127,61 @@ namespace Orchard.Environment.Extensions { References = references }); } + + // Keep track of which loader we use for every extension + // This will be needed for processing references from other dependent extensions + context.ProcessedExtensions.Add(extension.Name, activatedExtension); + } + + private ExtensionLoadingContext CreateLoadingContext() { + var availableExtensions = _extensionManager + .AvailableExtensions() + .Where(d => d.ExtensionType == "Module") + .OrderBy(d => d.Name) + .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 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); + + var sortedAvailableExtensions = + availableExtensions.OrderByDependencies( + (item, dep) => referencesByModule.ContainsKey(item.Name) && + referencesByModule[item.Name].Any(r => StringComparer.OrdinalIgnoreCase.Equals(dep.Name, r.Name))) + .ToList(); + + return new ExtensionLoadingContext { + AvailableExtensions = sortedAvailableExtensions, + PreviousDependencies = previousDependencies, + DeletedDependencies = deletedDependencies, + AvailableExtensionsProbes = availableExtensionsProbes, + ReferencesByName = referencesByName, + ReferencesByModule = referencesByModule + }; } IEnumerable ProcessExtensionReferences(ExtensionLoadingContext context, ExtensionProbeEntry activatedExtension) { @@ -193,16 +224,16 @@ namespace Orchard.Environment.Extensions { .OrderBy(e => e.LastWriteTimeUtc) .ThenBy(e => e.Entry.Name).FirstOrDefault(); - var probes = context.AvailableExtensionsProbes.ContainsKey(referenceName) ? - context.AvailableExtensionsProbes[referenceName] : - Enumerable.Empty(); - var bestProbe = probes.FirstOrDefault(); + var bestProbe = context.ProcessedExtensions.ContainsKey(referenceName) ? + context.ProcessedExtensions[referenceName] : + null; // Pick the best one of module vs binary if (bestProbe != null && bestBinaryReference != null) { if (bestProbe.LastModificationTimeUtc >= bestBinaryReference.LastWriteTimeUtc) { bestBinaryReference = null; - } else { + } + else { bestProbe = null; } } @@ -222,8 +253,7 @@ namespace Orchard.Environment.Extensions { } // Activated the module ref - if (bestProbe != null) - { + if (bestProbe != null) { activatedReferences.Add(new DependencyReferenceDescriptor { LoaderName = bestProbe.Loader.Name, Name = referenceName, diff --git a/src/Orchard/Environment/Extensions/ExtensionLoadingContext.cs b/src/Orchard/Environment/Extensions/ExtensionLoadingContext.cs index 7988757ae..bb9a11af5 100644 --- a/src/Orchard/Environment/Extensions/ExtensionLoadingContext.cs +++ b/src/Orchard/Environment/Extensions/ExtensionLoadingContext.cs @@ -8,14 +8,14 @@ using Orchard.FileSystems.Dependencies; namespace Orchard.Environment.Extensions { public class ExtensionLoadingContext { public ExtensionLoadingContext() { - ProcessedExtensions = new HashSet(StringComparer.OrdinalIgnoreCase); + ProcessedExtensions = new Dictionary(StringComparer.OrdinalIgnoreCase); ProcessedReferences = new HashSet(StringComparer.OrdinalIgnoreCase); DeleteActions = new List(); CopyActions = new List(); NewDependencies = new List(); } - public ISet ProcessedExtensions { get; private set; } + public IDictionary ProcessedExtensions { get; private set; } public ISet ProcessedReferences { get; private set; } public IList NewDependencies { get; private set; } diff --git a/src/Orchard/Environment/Extensions/ExtensionManager.cs b/src/Orchard/Environment/Extensions/ExtensionManager.cs index 4d2cb8de3..ac8ad567c 100644 --- a/src/Orchard/Environment/Extensions/ExtensionManager.cs +++ b/src/Orchard/Environment/Extensions/ExtensionManager.cs @@ -36,7 +36,9 @@ namespace Orchard.Environment.Extensions { foreach (var descriptor in AvailableExtensions()) { // Extensions that are Themes don't have buildable components. if (String.Equals(descriptor.ExtensionType, "Module", StringComparison.OrdinalIgnoreCase)) { - yield return BuildEntry(descriptor); + var entry = BuildEntry(descriptor); + if (entry != null) + yield return entry; } } } @@ -159,6 +161,8 @@ namespace Orchard.Environment.Extensions { if (entry != null) return entry; } + + Logger.Warning("No suitable loader found for extension \"{0}\"", descriptor.Name); return null; } } diff --git a/src/Orchard/Environment/Extensions/Loaders/ExtensionLoaderBase.cs b/src/Orchard/Environment/Extensions/Loaders/ExtensionLoaderBase.cs index 09192bd06..d17b6926a 100644 --- a/src/Orchard/Environment/Extensions/Loaders/ExtensionLoaderBase.cs +++ b/src/Orchard/Environment/Extensions/Loaders/ExtensionLoaderBase.cs @@ -25,6 +25,10 @@ namespace Orchard.Environment.Extensions.Loaders { return null; } + public virtual bool IsCompatibleWithReferences(ExtensionDescriptor extension, IEnumerable references) { + return true; + } + public abstract ExtensionProbeEntry Probe(ExtensionDescriptor descriptor); public ExtensionEntry Load(ExtensionDescriptor descriptor) { diff --git a/src/Orchard/Environment/Extensions/Loaders/IExtensionLoader.cs b/src/Orchard/Environment/Extensions/Loaders/IExtensionLoader.cs index 6f6203398..f066f9616 100644 --- a/src/Orchard/Environment/Extensions/Loaders/IExtensionLoader.cs +++ b/src/Orchard/Environment/Extensions/Loaders/IExtensionLoader.cs @@ -28,6 +28,7 @@ namespace Orchard.Environment.Extensions.Loaders { Assembly LoadReference(DependencyReferenceDescriptor reference); void ReferenceActivated(ExtensionLoadingContext context, ExtensionReferenceProbeEntry referenceEntry); void ReferenceDeactivated(ExtensionLoadingContext context, ExtensionReferenceProbeEntry referenceEntry); + bool IsCompatibleWithReferences(ExtensionDescriptor extension, IEnumerable references); ExtensionProbeEntry Probe(ExtensionDescriptor descriptor); ExtensionEntry Load(ExtensionDescriptor descriptor); diff --git a/src/Orchard/Environment/Extensions/Loaders/PrecompiledExtensionLoader.cs b/src/Orchard/Environment/Extensions/Loaders/PrecompiledExtensionLoader.cs index 315cf5680..83e653fe0 100644 --- a/src/Orchard/Environment/Extensions/Loaders/PrecompiledExtensionLoader.cs +++ b/src/Orchard/Environment/Extensions/Loaders/PrecompiledExtensionLoader.cs @@ -142,6 +142,17 @@ namespace Orchard.Environment.Extensions.Loaders { } ); } + public override bool IsCompatibleWithReferences(ExtensionDescriptor extension, IEnumerable references) { + // A pre-compiled module is _not_ compatible with a dynamically loaded module + // because a pre-compiled module usually references a pre-compiled assembly binary + // which will have a different identity (i.e. name) from the dynamic module. + bool result = references.All(r => r.Loader.GetType() != typeof (DynamicExtensionLoader)); + if (!result) { + Logger.Information("Extension \"{0}\" will not be loaded as pre-compiled extension because one or more referenced extension is dynamically compiled", extension.Name); + } + return result; + } + public override ExtensionProbeEntry Probe(ExtensionDescriptor descriptor) { var assemblyPath = GetAssemblyPath(descriptor); if (assemblyPath == null) diff --git a/src/Orchard/Environment/Extensions/Loaders/ProbingExtensionLoader.cs b/src/Orchard/Environment/Extensions/Loaders/ProbingExtensionLoader.cs index bb2e82622..54fed4f1b 100644 --- a/src/Orchard/Environment/Extensions/Loaders/ProbingExtensionLoader.cs +++ b/src/Orchard/Environment/Extensions/Loaders/ProbingExtensionLoader.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using Orchard.Environment.Extensions.Models; using Orchard.FileSystems.Dependencies; using Orchard.Logging; @@ -64,6 +65,17 @@ namespace Orchard.Environment.Extensions.Loaders { } } + public override bool IsCompatibleWithReferences(ExtensionDescriptor extension, IEnumerable references) { + // A pre-compiled module is _not_ compatible with a dynamically loaded module + // because a pre-compiled module usually references a pre-compiled assembly binary + // which will have a different identity (i.e. name) from the dynamic module. + bool result = references.All(r => r.Loader.GetType() != typeof(DynamicExtensionLoader)); + if (!result) { + Logger.Information("Extension \"{0}\" will not be loaded as pre-compiled extension because one or more referenced extension is dynamically compiled", extension.Name); + } + return result; + } + public override ExtensionProbeEntry Probe(ExtensionDescriptor descriptor) { if (!_assemblyProbingFolder.AssemblyExists(descriptor.Name)) return null;