Example #1
0
        private static LoadPluginResult LoadPluginFromFolder(string pluginFolderPath, ICollection <string> installedPluginSystemNames)
        {
            Guard.ArgumentNotEmpty(() => pluginFolderPath);

            var folder = new DirectoryInfo(pluginFolderPath);

            if (!folder.Exists)
            {
                return(null);
            }

            var descriptionFile = new FileInfo(Path.Combine(pluginFolderPath, "Description.txt"));

            if (!descriptionFile.Exists)
            {
                return(null);
            }

            // load descriptor file (Description.txt)
            var descriptor = PluginFileParser.ParsePluginDescriptionFile(descriptionFile.FullName);

            // some validation
            if (descriptor.SystemName.IsEmpty())
            {
                throw new Exception("The plugin descriptor '{0}' does not define a plugin system name. Try assigning the plugin a unique name and recompile.".FormatInvariant(descriptionFile.FullName));
            }
            if (descriptor.PluginFileName.IsEmpty())
            {
                throw new Exception("The plugin descriptor '{0}' does not define a plugin assembly file name. Try assigning the plugin a file name and recompile.".FormatInvariant(descriptionFile.FullName));
            }

            var result = new LoadPluginResult
            {
                DescriptionFile = descriptionFile,
                Descriptor      = descriptor
            };

            //ensure that version of plugin is valid
            if (!IsAssumedCompatible(descriptor))
            {
                result.IsIncompatible = true;
                return(result);
            }

            if (_referencedPlugins.ContainsKey(descriptor.SystemName))
            {
                throw new Exception(string.Format("A plugin with system name '{0}' is already defined", descriptor.SystemName));
            }

            if (installedPluginSystemNames == null)
            {
                installedPluginSystemNames = PluginFileParser.ParseInstalledPluginsFile();
            }

            // set 'Installed' property
            descriptor.Installed = installedPluginSystemNames.Contains(descriptor.SystemName);

            try
            {
                // get list of all DLLs in plugin folders (not in 'bin' or '_Backup'!)
                var pluginBinaries = descriptionFile.Directory.GetFiles("*.dll", SearchOption.AllDirectories)
                                     // just make sure we're not registering shadow copied plugins
                                     .Where(x => IsPackagePluginFolder(x.Directory))
                                     .ToList();

                // other plugin description info
                var mainPluginFile = pluginBinaries.Where(x => x.Name.IsCaseInsensitiveEqual(descriptor.PluginFileName)).FirstOrDefault();
                descriptor.OriginalAssemblyFile = mainPluginFile;

                // shadow copy main plugin file
                descriptor.ReferencedAssembly = Probe(mainPluginFile);

                if (!descriptor.Installed)
                {
                    _inactiveAssemblies.Add(descriptor.ReferencedAssembly);
                }

                // load all other referenced assemblies now
                var otherAssemblies = from x in pluginBinaries
                                      where !x.Name.IsCaseInsensitiveEqual(mainPluginFile.Name)
                                      select x;

                foreach (var assemblyFile in otherAssemblies)
                {
                    if (!IsAlreadyLoaded(assemblyFile))
                    {
                        Probe(assemblyFile);
                    }
                }

                // init plugin type (only one plugin per assembly is allowed)
                var  exportedTypes   = descriptor.ReferencedAssembly.ExportedTypes;
                bool pluginFound     = false;
                bool preStarterFound = !descriptor.Installed;
                foreach (var t in exportedTypes)
                {
                    if (typeof(IPlugin).IsAssignableFrom(t) && !t.IsInterface && t.IsClass && !t.IsAbstract)
                    {
                        descriptor.PluginType     = t;
                        descriptor.IsConfigurable = typeof(IConfigurable).IsAssignableFrom(t);
                        pluginFound = true;
                    }
                    else if (descriptor.Installed && typeof(IPreApplicationStart).IsAssignableFrom(t) && !t.IsInterface && t.IsClass && !t.IsAbstract && t.HasDefaultConstructor())
                    {
                        try
                        {
                            var preStarter = Activator.CreateInstance(t) as IPreApplicationStart;
                            preStarter.Start();
                        }
                        catch { }
                        preStarterFound = true;
                    }
                    if (pluginFound && preStarterFound)
                    {
                        break;
                    }
                }

                result.Success = true;
            }
            catch (ReflectionTypeLoadException ex)
            {
                var msg = string.Empty;
                foreach (var e in ex.LoaderExceptions)
                {
                    msg += e.Message + Environment.NewLine;
                }

                var fail = new Exception(msg, ex);
                Debug.WriteLine(fail.Message, fail);

                throw fail;
            }

            return(result);
        }
Example #2
0
        /// <summary>
        /// Initialize
        /// </summary>
        public static void Initialize()
        {
            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/amd64)
            // ensures that unmanaged native dependencies can be resolved successfully.
            SetPrivateEnvPath();

            DynamicModuleUtility.RegisterModule(typeof(AutofacRequestLifetimeHttpModule));

            using (Locker.GetWriteLock())
            {
                // TODO: Add verbose exception handling / raising here since this is happening on app startup and could
                // prevent app from starting altogether
                var pluginFolderPath = CommonHelper.MapPath(_pluginsPath);
                _shadowCopyFolder = new DirectoryInfo(CommonHelper.MapPath(_shadowCopyPath));

                var incompatiblePlugins = new List <string>();
                _clearShadowDirectoryOnStartup = CommonHelper.GetAppSetting <bool>("sm:ClearPluginsShadowDirectoryOnStartup", true);
                try
                {
                    Debug.WriteLine("Creating shadow copy folder and querying for dlls");
                    //ensure folders are created
                    Directory.CreateDirectory(pluginFolderPath);
                    Directory.CreateDirectory(_shadowCopyFolder.FullName);

                    // get list of all files in bin
                    var binFiles = _shadowCopyFolder.GetFiles("*", SearchOption.AllDirectories);
                    if (_clearShadowDirectoryOnStartup)
                    {
                        // clear out shadow copied plugins
                        foreach (var f in binFiles)
                        {
                            Debug.WriteLine("Deleting " + f.Name);
                            try
                            {
                                File.Delete(f.FullName);
                            }
                            catch (Exception exc)
                            {
                                Debug.WriteLine("Error deleting file " + f.Name + ". Exception: " + exc);
                            }
                        }
                    }

                    // determine all plugin folders
                    var pluginPaths = from x in Directory.EnumerateDirectories(pluginFolderPath)
                                      where !x.IsMatch("bin") && !x.IsMatch("_Backup")
                                      select Path.Combine(pluginFolderPath, x);

                    var installedPluginSystemNames = PluginFileParser.ParseInstalledPluginsFile();

                    // now activate all plugins
                    foreach (var pluginPath in pluginPaths)
                    {
                        var result = LoadPluginFromFolder(pluginPath, installedPluginSystemNames);
                        if (result != null)
                        {
                            if (result.IsIncompatible)
                            {
                                incompatiblePlugins.Add(result.Descriptor.SystemName);
                            }
                            else if (result.Success)
                            {
                                _referencedPlugins[result.Descriptor.SystemName] = result.Descriptor;
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    var msg = string.Empty;
                    for (var e = ex; e != null; e = e.InnerException)
                    {
                        msg += e.Message + Environment.NewLine;
                    }

                    var fail = new Exception(msg, ex);
                    Debug.WriteLine(fail.Message, fail);

                    throw fail;
                }

                IncompatiblePlugins = incompatiblePlugins.AsReadOnly();
            }
        }
Example #3
0
        ///// <summary>
        ///// Loads and parses the descriptors of all installed plugins
        ///// </summary>
        ///// <returns>All descriptors</returns>
        //private static Task ReadPluginDescriptors(BlockingCollection<PluginDescriptor> bag)
        //{
        //	// TODO: Add verbose exception handling / raising here since this is happening on app startup and could
        //	// prevent app from starting altogether

        //	return Task.Factory.StartNew(() =>
        //	{
        //		var pluginsDir = new DirectoryInfo(CommonHelper.MapPath(_pluginsPath));

        //		if (!pluginsDir.Exists)
        //		{
        //			pluginsDir.Create();
        //		}
        //		else
        //		{
        //			// Determine all plugin folders: ~/Plugins/{SystemName}
        //			var allPluginDirs = pluginsDir.EnumerateDirectories().ToArray()
        //				.Where(x => !x.Name.IsMatch("bin") && !x.Name.IsMatch("_Backup"))
        //				.OrderBy(x => x.Name)
        //				.ToArray();

        //			var installedPluginSystemNames = PluginFileParser.ParseInstalledPluginsFile().AsSynchronized();

        //			// Load/activate all plugins
        //			allPluginDirs
        //				.AsParallel()
        //				.AsOrdered()
        //				.Select(d => LoadPluginDescriptor(d, installedPluginSystemNames))
        //				.Where(x => x != null)
        //				.ForAll(x => bag.Add(x));
        //		}

        //		bag.CompleteAdding();
        //	});
        //}

        private static PluginDescriptor LoadPluginDescriptor(DirectoryInfo d, ICollection <string> installedPluginSystemNames)
        {
            var descriptionFile = new FileInfo(Path.Combine(d.FullName, "Description.txt"));

            if (!descriptionFile.Exists)
            {
                return(null);
            }

            // Load descriptor file (Description.txt)
            var descriptor = PluginFileParser.ParsePluginDescriptionFile(descriptionFile.FullName);

            // Some validation
            if (descriptor.SystemName.IsEmpty())
            {
                throw new SmartException("The plugin descriptor '{0}' does not define a plugin system name. Try assigning the plugin a unique name and recompile.".FormatInvariant(descriptionFile.FullName));
            }

            if (descriptor.PluginFileName.IsEmpty())
            {
                throw new SmartException("The plugin descriptor '{0}' does not define a plugin assembly file name. Try assigning the plugin a file name and recompile.".FormatInvariant(descriptionFile.FullName));
            }

            descriptor.VirtualPath = PluginsLocation + "/" + descriptor.FolderName;

            // Set 'Installed' property
            descriptor.Installed = installedPluginSystemNames.Contains(descriptor.SystemName);

            // Ensure that version of plugin is valid
            if (!IsAssumedCompatible(descriptor))
            {
                // Set 'Incompatible' property and return
                descriptor.Incompatible = true;
                return(descriptor);
            }

            var skipDlls = new HashSet <string>(new[] { "log4net.dll" }, StringComparer.OrdinalIgnoreCase);

            // Get list of all DLLs in plugin folders (not in 'bin' or '_Backup'!)
            var pluginBinaries = descriptionFile.Directory.EnumerateFiles("*.dll", SearchOption.AllDirectories).ToArray()
                                 .Where(x => IsPackagePluginFolder(x.Directory) && !skipDlls.Contains(x.Name))
                                 .OrderBy(x => x.Name)
                                 .ToDictionarySafe(x => x.Name, StringComparer.OrdinalIgnoreCase);

            // Set 'OriginalAssemblyFile' property
            descriptor.Assembly.OriginalFile = pluginBinaries.Get(descriptor.PluginFileName);

            if (descriptor.Assembly.OriginalFile == null)
            {
                throw new SmartException("The main assembly '{0}' for plugin '{1}' could not be found.".FormatInvariant(descriptor.PluginFileName, descriptor.SystemName));
            }

            // Load all other referenced local assemblies now
            var otherAssemblyFiles = pluginBinaries
                                     .Where(x => !x.Key.IsCaseInsensitiveEqual(descriptor.PluginFileName))
                                     .Select(x => x.Value);

            descriptor.ReferencedLocalAssemblies = otherAssemblyFiles.Select(x => new AssemblyReference {
                OriginalFile = x
            }).ToArray();

            return(descriptor);
        }