Preventing modules from breaking the website if a referenced assembly is missing

--HG--
branch : 1.x
This commit is contained in:
Sebastien Ros
2011-11-30 17:43:09 -08:00
parent f5810823fb
commit 9380e2c4a6
11 changed files with 94 additions and 26 deletions

View File

@@ -0,0 +1,24 @@
using System.Linq;
using System.Collections.Generic;
using Orchard.Environment.Extensions;
using Orchard.Localization;
using Orchard.UI.Admin.Notification;
using Orchard.UI.Notify;
namespace Orchard.Core.Dashboard.Services {
public class CompilationErrorBanner : INotificationProvider {
private readonly ICriticalErrorProvider _errorProvider;
public CompilationErrorBanner(ICriticalErrorProvider errorProvider) {
_errorProvider = errorProvider;
T = NullLocalizer.Instance;
}
public Localizer T { get; set; }
public IEnumerable<NotifyEntry> GetNotifications() {
return _errorProvider.GetErrors()
.Select(message => new NotifyEntry { Message = message, Type = NotifyType.Error });
}
}
}

View File

@@ -124,6 +124,7 @@
<Compile Include="Contents\Settings\ContentPartSettings.cs" /> <Compile Include="Contents\Settings\ContentPartSettings.cs" />
<Compile Include="Contents\Shapes.cs" /> <Compile Include="Contents\Shapes.cs" />
<Compile Include="Contents\ViewModels\PublishContentViewModel.cs" /> <Compile Include="Contents\ViewModels\PublishContentViewModel.cs" />
<Compile Include="Dashboard\Services\CompilationErrorBanner.cs" />
<Compile Include="Navigation\Commands\MenuCommands.cs" /> <Compile Include="Navigation\Commands\MenuCommands.cs" />
<Compile Include="Navigation\Drivers\AdminMenuPartDriver.cs" /> <Compile Include="Navigation\Drivers\AdminMenuPartDriver.cs" />
<Compile Include="Navigation\Handlers\AdminMenuPartHandler.cs" /> <Compile Include="Navigation\Handlers\AdminMenuPartHandler.cs" />

View File

@@ -1 +1 @@
<div class="message message-@Model.Type">@Model.Message</div> <div class="message message-@Model.Type">@Html.Raw(Html.Encode(Model.Message).Replace("\n", "<br />"))</div>

View File

@@ -1,6 +1,7 @@
using System.Collections.Concurrent;
using System.Linq; using System.Linq;
using System.Collections.Generic; using System.Collections.Generic;
using System.Transactions;
using Orchard.Caching; using Orchard.Caching;
using Orchard.Environment.Configuration; using Orchard.Environment.Configuration;
using Orchard.Environment.Extensions; using Orchard.Environment.Extensions;
@@ -13,7 +14,7 @@ using Orchard.Logging;
using Orchard.Utility.Extensions; using Orchard.Utility.Extensions;
namespace Orchard.Environment { namespace Orchard.Environment {
public class DefaultOrchardHost : IOrchardHost, IShellSettingsManagerEventHandler, IShellDescriptorManagerEventHandler { public class DefaultOrchardHost : IOrchardHost, IShellSettingsManagerEventHandler, IShellDescriptorManagerEventHandler, ICriticalErrorProvider {
private readonly IHostLocalRestart _hostLocalRestart; private readonly IHostLocalRestart _hostLocalRestart;
private readonly IShellSettingsManager _shellSettingsManager; private readonly IShellSettingsManager _shellSettingsManager;
private readonly IShellContextFactory _shellContextFactory; private readonly IShellContextFactory _shellContextFactory;
@@ -22,6 +23,7 @@ namespace Orchard.Environment {
private readonly IExtensionLoaderCoordinator _extensionLoaderCoordinator; private readonly IExtensionLoaderCoordinator _extensionLoaderCoordinator;
private readonly IExtensionMonitoringCoordinator _extensionMonitoringCoordinator; private readonly IExtensionMonitoringCoordinator _extensionMonitoringCoordinator;
private readonly ICacheManager _cacheManager; private readonly ICacheManager _cacheManager;
private readonly ConcurrentBag<LocalizedString> _errorMessages;
private readonly object _syncLock = new object(); private readonly object _syncLock = new object();
private IEnumerable<ShellContext> _shellContexts; private IEnumerable<ShellContext> _shellContexts;
@@ -45,6 +47,7 @@ namespace Orchard.Environment {
_cacheManager = cacheManager; _cacheManager = cacheManager;
_hostLocalRestart = hostLocalRestart; _hostLocalRestart = hostLocalRestart;
_tenantsToRestart = Enumerable.Empty<ShellSettings>(); _tenantsToRestart = Enumerable.Empty<ShellSettings>();
_errorMessages = new ConcurrentBag<LocalizedString>();
T = NullLocalizer.Instance; T = NullLocalizer.Instance;
Logger = NullLogger.Instance; Logger = NullLogger.Instance;
@@ -57,6 +60,12 @@ namespace Orchard.Environment {
get { return BuildCurrent().ToReadOnlyCollection(); } get { return BuildCurrent().ToReadOnlyCollection(); }
} }
public void RegisterErrorMessage(LocalizedString message) {
if (_errorMessages != null && _errorMessages.All(m => m.TextHint != message.TextHint)) {
_errorMessages.Add(message);
}
}
public ShellContext GetShellContext(ShellSettings shellSettings) { public ShellContext GetShellContext(ShellSettings shellSettings) {
return Current return Current
.Single(shellContext => shellContext.Settings.Name.Equals(shellSettings.Name)); .Single(shellContext => shellContext.Settings.Name.Equals(shellSettings.Name));
@@ -124,7 +133,7 @@ namespace Orchard.Environment {
Logger.Information("Start creation of shells"); Logger.Information("Start creation of shells");
// is there any tenant right now ? // is there any tenant right now ?
var allSettings = _shellSettingsManager.LoadSettings(); var allSettings = _shellSettingsManager.LoadSettings().ToArray();
// load all tenants, and activate their shell // load all tenants, and activate their shell
if (allSettings.Any()) { if (allSettings.Any()) {
@@ -261,12 +270,12 @@ namespace Orchard.Environment {
/// </summary> /// </summary>
void IShellDescriptorManagerEventHandler.Changed(ShellDescriptor descriptor, string tenant) { void IShellDescriptorManagerEventHandler.Changed(ShellDescriptor descriptor, string tenant) {
lock (_syncLock) { lock (_syncLock) {
var context = _shellContexts.Where(x => x.Settings.Name == tenant).FirstOrDefault(); var context = _shellContexts.FirstOrDefault(x => x.Settings.Name == tenant);
// some shells might need to be started, e.g. created by command line // some shells might need to be started, e.g. created by command line
if(context == null) { if(context == null) {
StartUpdatedShells(); StartUpdatedShells();
context = _shellContexts.Where(x => x.Settings.Name == tenant).First(); context = _shellContexts.First(x => x.Settings.Name == tenant);
} }
// don't update the settings themselves here // don't update the settings themselves here
@@ -277,5 +286,9 @@ namespace Orchard.Environment {
_tenantsToRestart = _tenantsToRestart.Union(new[] { context.Settings }); _tenantsToRestart = _tenantsToRestart.Union(new[] { context.Settings });
} }
} }
public IEnumerable<LocalizedString> GetErrors() {
return _errorMessages;
}
} }
} }

View File

@@ -19,19 +19,22 @@ namespace Orchard.Environment.Extensions.Compilers {
private readonly IDependenciesFolder _dependenciesFolder; private readonly IDependenciesFolder _dependenciesFolder;
private readonly IEnumerable<IExtensionLoader> _loaders; private readonly IEnumerable<IExtensionLoader> _loaders;
private readonly IAssemblyLoader _assemblyLoader; private readonly IAssemblyLoader _assemblyLoader;
private readonly IOrchardHost _orchardHost;
public DefaultExtensionCompiler( public DefaultExtensionCompiler(
IVirtualPathProvider virtualPathProvider, IVirtualPathProvider virtualPathProvider,
IProjectFileParser projectFileParser, IProjectFileParser projectFileParser,
IDependenciesFolder dependenciesFolder, IDependenciesFolder dependenciesFolder,
IEnumerable<IExtensionLoader> loaders, IEnumerable<IExtensionLoader> loaders,
IAssemblyLoader assemblyLoader) { IAssemblyLoader assemblyLoader,
IOrchardHost orchardHost) {
_virtualPathProvider = virtualPathProvider; _virtualPathProvider = virtualPathProvider;
_projectFileParser = projectFileParser; _projectFileParser = projectFileParser;
_dependenciesFolder = dependenciesFolder; _dependenciesFolder = dependenciesFolder;
_loaders = loaders; _loaders = loaders;
_assemblyLoader = assemblyLoader; _assemblyLoader = assemblyLoader;
_orchardHost = orchardHost;
T = NullLocalizer.Instance; T = NullLocalizer.Instance;
Logger = NullLogger.Instance; Logger = NullLogger.Instance;
@@ -88,14 +91,16 @@ namespace Orchard.Environment.Extensions.Compilers {
} }
else { else {
Logger.Error("Assembly reference '{0}' for project '{1}' cannot be loaded", assemblyReference.FullName, context.VirtualPath); Logger.Error("Assembly reference '{0}' for project '{1}' cannot be loaded", assemblyReference.FullName, context.VirtualPath);
throw new OrchardCoreException(T( _orchardHost.RegisterErrorMessage(T(
"The assembly reference '{0}' could not be loaded.\r\n\r\n" + "The assembly reference '{0}' could not be loaded.\r\n\r\n" +
"There are generally a few ways to solve this issue:\r\n" + "There are generally a few ways to solve this issue:\r\n" +
"1. Remove the assembly reference from the project file if it's not needed.\r\n" + "1. Install any dependent module.\r\n" +
"2. Ensure the assembly reference is present in the 'bin' directory of the module.\r\n" + "2. Remove the assembly reference from the project file if it's not needed.\r\n" +
"3. Ensure the assembly reference is present in the 'bin' directory of the application.\r\n" + "3. Ensure the assembly reference is present in the 'bin' directory of the module.\r\n" +
"4. Specify the strong name of the assembly (name, version, culture, publickey) if the assembly is present in the GAC.", "4. Ensure the assembly reference is present in the 'bin' directory of the application.\r\n" +
"5. Specify the strong name of the assembly (name, version, culture, publickey) if the assembly is present in the GAC.",
assemblyReference.FullName)); assemblyReference.FullName));
throw new OrchardCoreException(T("Assembly reference '{0}' for project '{1}' cannot be loaded", assemblyReference.FullName, context.VirtualPath));
} }
} }
} }

View File

@@ -109,6 +109,9 @@ namespace Orchard.Environment.Extensions {
context.AvailableExtensionsProbes[extension.Id] : context.AvailableExtensionsProbes[extension.Id] :
Enumerable.Empty<ExtensionProbeEntry>(); Enumerable.Empty<ExtensionProbeEntry>();
// materializes the list
extensionProbes = extensionProbes.ToArray();
if (Logger.IsEnabled(LogLevel.Debug)) { if (Logger.IsEnabled(LogLevel.Debug)) {
Logger.Debug("Loaders for extension \"{0}\": ", extension.Id); Logger.Debug("Loaders for extension \"{0}\": ", extension.Id);
foreach (var probe in extensionProbes) { foreach (var probe in extensionProbes) {
@@ -131,14 +134,13 @@ namespace Orchard.Environment.Extensions {
.Select(e => context.ProcessedExtensions[e.Id]) .Select(e => context.ProcessedExtensions[e.Id])
.ToList(); .ToList();
var activatedExtension = extensionProbes var activatedExtension = extensionProbes.FirstOrDefault(
.Where(e => e.Loader.IsCompatibleWithModuleReferences(extension, processedModuleReferences)) e => e.Loader.IsCompatibleWithModuleReferences(extension, processedModuleReferences)
.FirstOrDefault(); );
var previousDependency = context var previousDependency = context.PreviousDependencies.FirstOrDefault(
.PreviousDependencies d => StringComparer.OrdinalIgnoreCase.Equals(d.Name, extension.Id)
.Where(d => StringComparer.OrdinalIgnoreCase.Equals(d.Name, extension.Id)) );
.FirstOrDefault();
if (activatedExtension == null) { if (activatedExtension == null) {
Logger.Warning("No loader found for extension \"{0}\"!", extension.Id); Logger.Warning("No loader found for extension \"{0}\"!", extension.Id);
@@ -180,7 +182,7 @@ namespace Orchard.Environment.Extensions {
// Check there are no duplicates // Check there are no duplicates
var duplicates = availableExtensions.GroupBy(ed => ed.Id).Where(g => g.Count() >= 2).ToList(); var duplicates = availableExtensions.GroupBy(ed => ed.Id).Where(g => g.Count() >= 2).ToList();
if (duplicates.Count() > 0) { if (duplicates.Any()) {
var sb = new StringBuilder(); var sb = new StringBuilder();
sb.Append(T("There are multiple extensions with the same name installed in this instance of Orchard.\r\n")); sb.Append(T("There are multiple extensions with the same name installed in this instance of Orchard.\r\n"));
foreach (var dup in duplicates) { foreach (var dup in duplicates) {
@@ -232,7 +234,7 @@ namespace Orchard.Environment.Extensions {
availableExtensions.OrderByDependenciesAndPriorities( availableExtensions.OrderByDependenciesAndPriorities(
(item, dep) => referencesByModule.ContainsKey(item.Id) && (item, dep) => referencesByModule.ContainsKey(item.Id) &&
referencesByModule[item.Id].Any(r => StringComparer.OrdinalIgnoreCase.Equals(dep.Id, r.Name)), referencesByModule[item.Id].Any(r => StringComparer.OrdinalIgnoreCase.Equals(dep.Id, r.Name)),
(item) => 0) item => 0)
.ToList(); .ToList();
return new ExtensionLoadingContext { return new ExtensionLoadingContext {
@@ -253,7 +255,7 @@ namespace Orchard.Environment.Extensions {
.OrderByDescending(g => g.Key); .OrderByDescending(g => g.Key);
// Select highest priority group with at least one item // Select highest priority group with at least one item
var firstNonEmptyGroup = groupByPriority.FirstOrDefault(g => g.Count() >= 1) ?? Enumerable.Empty<ExtensionProbeEntry>(); var firstNonEmptyGroup = groupByPriority.FirstOrDefault(g => g.Any()) ?? Enumerable.Empty<ExtensionProbeEntry>();
// No need for further sorting if only 1 item found // No need for further sorting if only 1 item found
if (firstNonEmptyGroup.Count() <= 1) if (firstNonEmptyGroup.Count() <= 1)

View File

@@ -0,0 +1,8 @@
using System.Collections.Generic;
using Orchard.Localization;
namespace Orchard.Environment.Extensions {
public interface ICriticalErrorProvider {
IEnumerable<LocalizedString> GetErrors();
}
}

View File

@@ -16,7 +16,10 @@ namespace Orchard.Environment {
private readonly IVirtualPathProvider _virtualPathProvider; private readonly IVirtualPathProvider _virtualPathProvider;
private readonly IAssemblyLoader _assemblyLoader; private readonly IAssemblyLoader _assemblyLoader;
public DefaultBuildManager(IVirtualPathProvider virtualPathProvider, IAssemblyLoader assemblyLoader) { public DefaultBuildManager(
IVirtualPathProvider virtualPathProvider,
IAssemblyLoader assemblyLoader) {
_virtualPathProvider = virtualPathProvider; _virtualPathProvider = virtualPathProvider;
_assemblyLoader = assemblyLoader; _assemblyLoader = assemblyLoader;
} }
@@ -39,7 +42,13 @@ namespace Orchard.Environment {
public Assembly GetCompiledAssembly(string virtualPath) { public Assembly GetCompiledAssembly(string virtualPath) {
return BuildManager.GetCompiledAssembly(virtualPath); try {
return BuildManager.GetCompiledAssembly(virtualPath);
}
catch {
return null;
}
} }
} }
} }

View File

@@ -1,6 +1,6 @@
using System.Collections.Generic;
using Orchard.Environment.Configuration; using Orchard.Environment.Configuration;
using Orchard.Environment.ShellBuilders; using Orchard.Environment.ShellBuilders;
using Orchard.Localization;
namespace Orchard.Environment { namespace Orchard.Environment {
public interface IOrchardHost { public interface IOrchardHost {
@@ -25,6 +25,11 @@ namespace Orchard.Environment {
/// </summary> /// </summary>
void EndRequest(); void EndRequest();
/// <summary>
/// Called by any to notice the system of a critical issue at the system level, e.g. incorrect extensions
/// </summary>
void RegisterErrorMessage(LocalizedString message);
ShellContext GetShellContext(ShellSettings shellSettings); ShellContext GetShellContext(ShellSettings shellSettings);
/// <summary> /// <summary>

View File

@@ -71,7 +71,7 @@ namespace Orchard.Environment {
RegisterVolatileProvider<DefaultVirtualPathMonitor, IVirtualPathMonitor>(builder); RegisterVolatileProvider<DefaultVirtualPathMonitor, IVirtualPathMonitor>(builder);
RegisterVolatileProvider<DefaultVirtualPathProvider, IVirtualPathProvider>(builder); RegisterVolatileProvider<DefaultVirtualPathProvider, IVirtualPathProvider>(builder);
builder.RegisterType<DefaultOrchardHost>().As<IOrchardHost>().As<IEventHandler>().SingleInstance(); builder.RegisterType<DefaultOrchardHost>().As<IOrchardHost>().As<IEventHandler>().As<ICriticalErrorProvider>().SingleInstance();
{ {
builder.RegisterType<ShellSettingsManager>().As<IShellSettingsManager>().SingleInstance(); builder.RegisterType<ShellSettingsManager>().As<IShellSettingsManager>().SingleInstance();

View File

@@ -218,6 +218,7 @@
<Compile Include="Environment\Features\FeatureManager.cs" /> <Compile Include="Environment\Features\FeatureManager.cs" />
<Compile Include="Environment\IAssemblyLoader.cs" /> <Compile Include="Environment\IAssemblyLoader.cs" />
<Compile Include="Environment\HostComponentsConfigModule.cs" /> <Compile Include="Environment\HostComponentsConfigModule.cs" />
<Compile Include="Environment\Extensions\ICriticalErrorProvider.cs" />
<Compile Include="Environment\IOrchardFrameworkAssemblies.cs" /> <Compile Include="Environment\IOrchardFrameworkAssemblies.cs" />
<Compile Include="Environment\ViewsBackgroundCompilation.cs" /> <Compile Include="Environment\ViewsBackgroundCompilation.cs" />
<Compile Include="Environment\Warmup\WarmupUtility.cs" /> <Compile Include="Environment\Warmup\WarmupUtility.cs" />