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\Shapes.cs" />
<Compile Include="Contents\ViewModels\PublishContentViewModel.cs" />
<Compile Include="Dashboard\Services\CompilationErrorBanner.cs" />
<Compile Include="Navigation\Commands\MenuCommands.cs" />
<Compile Include="Navigation\Drivers\AdminMenuPartDriver.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.Collections.Generic;
using System.Transactions;
using Orchard.Caching;
using Orchard.Environment.Configuration;
using Orchard.Environment.Extensions;
@@ -13,7 +14,7 @@ using Orchard.Logging;
using Orchard.Utility.Extensions;
namespace Orchard.Environment {
public class DefaultOrchardHost : IOrchardHost, IShellSettingsManagerEventHandler, IShellDescriptorManagerEventHandler {
public class DefaultOrchardHost : IOrchardHost, IShellSettingsManagerEventHandler, IShellDescriptorManagerEventHandler, ICriticalErrorProvider {
private readonly IHostLocalRestart _hostLocalRestart;
private readonly IShellSettingsManager _shellSettingsManager;
private readonly IShellContextFactory _shellContextFactory;
@@ -22,6 +23,7 @@ namespace Orchard.Environment {
private readonly IExtensionLoaderCoordinator _extensionLoaderCoordinator;
private readonly IExtensionMonitoringCoordinator _extensionMonitoringCoordinator;
private readonly ICacheManager _cacheManager;
private readonly ConcurrentBag<LocalizedString> _errorMessages;
private readonly object _syncLock = new object();
private IEnumerable<ShellContext> _shellContexts;
@@ -45,6 +47,7 @@ namespace Orchard.Environment {
_cacheManager = cacheManager;
_hostLocalRestart = hostLocalRestart;
_tenantsToRestart = Enumerable.Empty<ShellSettings>();
_errorMessages = new ConcurrentBag<LocalizedString>();
T = NullLocalizer.Instance;
Logger = NullLogger.Instance;
@@ -57,6 +60,12 @@ namespace Orchard.Environment {
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) {
return Current
.Single(shellContext => shellContext.Settings.Name.Equals(shellSettings.Name));
@@ -124,7 +133,7 @@ namespace Orchard.Environment {
Logger.Information("Start creation of shells");
// is there any tenant right now ?
var allSettings = _shellSettingsManager.LoadSettings();
var allSettings = _shellSettingsManager.LoadSettings().ToArray();
// load all tenants, and activate their shell
if (allSettings.Any()) {
@@ -261,12 +270,12 @@ namespace Orchard.Environment {
/// </summary>
void IShellDescriptorManagerEventHandler.Changed(ShellDescriptor descriptor, string tenant) {
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
if(context == null) {
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
@@ -277,5 +286,9 @@ namespace Orchard.Environment {
_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 IEnumerable<IExtensionLoader> _loaders;
private readonly IAssemblyLoader _assemblyLoader;
private readonly IOrchardHost _orchardHost;
public DefaultExtensionCompiler(
IVirtualPathProvider virtualPathProvider,
IProjectFileParser projectFileParser,
IDependenciesFolder dependenciesFolder,
IEnumerable<IExtensionLoader> loaders,
IAssemblyLoader assemblyLoader) {
IAssemblyLoader assemblyLoader,
IOrchardHost orchardHost) {
_virtualPathProvider = virtualPathProvider;
_projectFileParser = projectFileParser;
_dependenciesFolder = dependenciesFolder;
_loaders = loaders;
_assemblyLoader = assemblyLoader;
_orchardHost = orchardHost;
T = NullLocalizer.Instance;
Logger = NullLogger.Instance;
@@ -88,14 +91,16 @@ namespace Orchard.Environment.Extensions.Compilers {
}
else {
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" +
"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" +
"2. Ensure the assembly reference is present in the 'bin' directory of the module.\r\n" +
"3. Ensure the assembly reference is present in the 'bin' directory of the application.\r\n" +
"4. Specify the strong name of the assembly (name, version, culture, publickey) if the assembly is present in the GAC.",
"1. Install any dependent 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 module.\r\n" +
"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));
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] :
Enumerable.Empty<ExtensionProbeEntry>();
// materializes the list
extensionProbes = extensionProbes.ToArray();
if (Logger.IsEnabled(LogLevel.Debug)) {
Logger.Debug("Loaders for extension \"{0}\": ", extension.Id);
foreach (var probe in extensionProbes) {
@@ -131,14 +134,13 @@ namespace Orchard.Environment.Extensions {
.Select(e => context.ProcessedExtensions[e.Id])
.ToList();
var activatedExtension = extensionProbes
.Where(e => e.Loader.IsCompatibleWithModuleReferences(extension, processedModuleReferences))
.FirstOrDefault();
var activatedExtension = extensionProbes.FirstOrDefault(
e => e.Loader.IsCompatibleWithModuleReferences(extension, processedModuleReferences)
);
var previousDependency = context
.PreviousDependencies
.Where(d => StringComparer.OrdinalIgnoreCase.Equals(d.Name, extension.Id))
.FirstOrDefault();
var previousDependency = context.PreviousDependencies.FirstOrDefault(
d => StringComparer.OrdinalIgnoreCase.Equals(d.Name, extension.Id)
);
if (activatedExtension == null) {
Logger.Warning("No loader found for extension \"{0}\"!", extension.Id);
@@ -180,7 +182,7 @@ namespace Orchard.Environment.Extensions {
// Check there are no duplicates
var duplicates = availableExtensions.GroupBy(ed => ed.Id).Where(g => g.Count() >= 2).ToList();
if (duplicates.Count() > 0) {
if (duplicates.Any()) {
var sb = new StringBuilder();
sb.Append(T("There are multiple extensions with the same name installed in this instance of Orchard.\r\n"));
foreach (var dup in duplicates) {
@@ -232,7 +234,7 @@ namespace Orchard.Environment.Extensions {
availableExtensions.OrderByDependenciesAndPriorities(
(item, dep) => referencesByModule.ContainsKey(item.Id) &&
referencesByModule[item.Id].Any(r => StringComparer.OrdinalIgnoreCase.Equals(dep.Id, r.Name)),
(item) => 0)
item => 0)
.ToList();
return new ExtensionLoadingContext {
@@ -253,7 +255,7 @@ namespace Orchard.Environment.Extensions {
.OrderByDescending(g => g.Key);
// 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
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 IAssemblyLoader _assemblyLoader;
public DefaultBuildManager(IVirtualPathProvider virtualPathProvider, IAssemblyLoader assemblyLoader) {
public DefaultBuildManager(
IVirtualPathProvider virtualPathProvider,
IAssemblyLoader assemblyLoader) {
_virtualPathProvider = virtualPathProvider;
_assemblyLoader = assemblyLoader;
}
@@ -39,7 +42,13 @@ namespace Orchard.Environment {
public Assembly GetCompiledAssembly(string 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.ShellBuilders;
using Orchard.Localization;
namespace Orchard.Environment {
public interface IOrchardHost {
@@ -25,6 +25,11 @@ namespace Orchard.Environment {
/// </summary>
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);
/// <summary>

View File

@@ -71,7 +71,7 @@ namespace Orchard.Environment {
RegisterVolatileProvider<DefaultVirtualPathMonitor, IVirtualPathMonitor>(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();

View File

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