private static void InitializeCore() { var isFullTrust = WebHelper.GetTrustLevel() == AspNetHostingPermissionLevel.Unrestricted; if (!isFullTrust) { throw new ApplicationException("Smartstore requires Full Trust mode. Please enable Full Trust for your web site or contact your hosting provider."); } using (var updater = new AppUpdater()) { // update from NuGet package, if it exists and is valid if (updater.TryUpdateFromPackage()) { // [...] } // execute migrations updater.ExecuteMigrations(); } // adding a process-specific environment path (either bin/x86 or bin/x64) // ensures that unmanaged native dependencies can be resolved successfully. SetNativeDllPath(); DynamicModuleUtility.RegisterModule(typeof(AutofacRequestLifetimeHttpModule)); var incompatiblePlugins = (new HashSet <string>(StringComparer.OrdinalIgnoreCase)).AsSynchronized(); var inactiveAssemblies = _inactiveAssemblies.AsSynchronized(); var dirty = false; var watch = Stopwatch.StartNew(); _shadowCopyDir = new DirectoryInfo(AppDomain.CurrentDomain.DynamicDirectory); var plugins = LoadPluginDescriptors().ToArray(); var compatiblePlugins = plugins.Where(x => !x.Incompatible).ToArray(); var hasher = new PluginsHasher(compatiblePlugins); Logger.DebugFormat("Loaded plugin descriptors. {0} total, {1} incompatible.", plugins.Length, plugins.Length - compatiblePlugins.Length); var ms = watch.ElapsedMilliseconds; Logger.DebugFormat("INIT PLUGINS (LoadPluginDescriptors). Time elapsed: {0} ms.", ms); // If plugins state is dirty, we copy files over to the dynamic folder, // otherwise we just reference the previously copied file. dirty = DetectAndCleanStalePlugins(compatiblePlugins, hasher); // Perf: Initialize/probe all plugins in parallel plugins.AsParallel().ForAll(x => { // Deploy to ASP.NET dynamic folder. DeployPlugin(x, dirty); // Finalize FinalizePlugin(x); }); //// Retry when failed, because during parallel execution assembly loading MAY fail. //// Therefore we retry initialization for failed plugins, but sequentially this time. //foreach (var p in plugins) //{ // // INFO: this seems redundant, but it's ok: // // DeployPlugin() only probes assemblies that are not loaded yet. // DeployPlugin(p, dirty); // // Finalize // FinalizePlugin(p); //} if (dirty && DataSettings.DatabaseIsInstalled()) { // Save current hash of all deployed plugins to disk hasher.Persist(); // Save names of all deployed assemblies to disk (so we can nuke them later) SavePluginsAssemblies(_referencedPlugins.Values); } IncompatiblePlugins = incompatiblePlugins.AsReadOnly(); ms = watch.ElapsedMilliseconds; Logger.DebugFormat("INIT PLUGINS (Deployment complete). Time elapsed: {0} ms.", ms); void DeployPlugin(PluginDescriptor p, bool shadowCopy) { if (p.Incompatible) { // Do nothing if plugin is incompatible return; } // First copy referenced local assemblies (if any) for (int i = 0; i < p.ReferencedLocalAssemblies.Length; ++i) { var refAr = p.ReferencedLocalAssemblies[i]; if (refAr.Assembly == null) { Probe(refAr, p, shadowCopy); } } // Then copy main plugin assembly var ar = p.Assembly; if (ar.Assembly == null) { Probe(ar, p, shadowCopy); if (ar.Assembly != null) { // Activate (even if uninstalled): Find IPlugin, IPreApplicationStart, IConfigurable etc. ActivatePlugin(p); } } } void FinalizePlugin(PluginDescriptor p) { _referencedPlugins[p.SystemName] = p; if (p.Incompatible) { incompatiblePlugins.Add(p.SystemName); return; } var firstFailedAssembly = p.ReferencedLocalAssemblies.FirstOrDefault(x => x.ActivationException != null); if (firstFailedAssembly == null && p.Assembly.ActivationException != null) { firstFailedAssembly = p.Assembly; } if (firstFailedAssembly != null) { Logger.ErrorFormat("Assembly probing failed for '{0}': {1}", firstFailedAssembly.File.Name, firstFailedAssembly.ActivationException.Message); p.Incompatible = true; incompatiblePlugins.Add(p.SystemName); } if ((!p.Installed || firstFailedAssembly != null) && p.Assembly.Assembly != null) { inactiveAssemblies.Add(p.Assembly.Assembly); } } }
/// <summary> /// This checks if any of our plugins have changed, if so it will go /// </summary> /// <returns> /// Returns true if there were changes to plugins and a cleanup was required, otherwise false is returned. /// </returns> private static bool DetectAndCleanStalePlugins(IEnumerable <PluginDescriptor> plugins, PluginsHasher hasher) { // Check if anything has been changed, or if we are in debug mode then always perform cleanup if (hasher.HasChanged) { PluginChangeDetected = true; Logger.DebugFormat("Plugin changes detected in hash."); var lastAssemblies = GetLastPluginsAssemblies(); // We need to read the old assembly list and clean them out from the shadow copy folder var staleFiles = lastAssemblies .Select(x => new FileInfo(Path.Combine(_shadowCopyDir.FullName, x))) .ToArray(); // If that fails, then we will try to remove it the hard way by renaming it to .delete if it doesn't delete nicely if (staleFiles.Length > 5) { staleFiles.AsParallel().ForAll(f => TryDeleteStaleFile(f)); } else { staleFiles.Each(f => TryDeleteStaleFile(f)); } return(true); } // No plugin changes found return(false); }