Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.PluginInfrastructure/Advanced/InstallationManager.cs @ 2688

Last change on this file since 2688 was 2688, checked in by gkronber, 14 years ago

Implemented an enumerable to iterate through all PluginFiles as suggested by swagner, replaced the Assemblies enumerable with an AssemblyName enumerable for internal usage in the plugin infrastructure and replaced Assembly.LoadFrom calls with Assembly.Load() to prevent loading from GAC as far as possible.

#850 (PluginInfrastructure should provide a way to get assemblies associated with a plug-in)

File size: 12.8 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2008 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
4 *
5 * This file is part of HeuristicLab.
6 *
7 * HeuristicLab is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HeuristicLab is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
19 */
20#endregion
21
22using System;
23using System.Collections.Generic;
24using System.Linq;
25using System.Text;
26using HeuristicLab.PluginInfrastructure.Manager;
27using System.IO;
28using System.ComponentModel;
29using HeuristicLab.PluginInfrastructure.UpdateLocationReference;
30using System.Reflection;
31
32namespace HeuristicLab.PluginInfrastructure.Advanced {
33  internal class InstallationManager {
34
35    internal event EventHandler<PluginInfrastructureCancelEventArgs> PreUpdatePlugin;
36    internal event EventHandler<PluginInfrastructureCancelEventArgs> PreRemovePlugin;
37    internal event EventHandler<PluginInfrastructureCancelEventArgs> PreInstallPlugin;
38
39    internal event EventHandler<PluginInfrastructureEventArgs> PluginUpdated;
40    internal event EventHandler<PluginInfrastructureEventArgs> PluginRemoved;
41    internal event EventHandler<PluginInfrastructureEventArgs> PluginInstalled;
42
43    private string pluginDir;
44    private string updateLocationUrl;
45    private PluginManager pluginManager;
46    public InstallationManager(string pluginDir) {
47      this.pluginDir = pluginDir;
48      this.updateLocationUrl = "http://localhost:59253/UpdateLocation.svc";
49      this.pluginManager = new PluginManager(pluginDir);
50      this.pluginManager.DiscoverAndCheckPlugins();
51    }
52
53    public IEnumerable<string> Show(IEnumerable<string> pluginNames) {
54      foreach (PluginDescription desc in GetPluginDescriptions(pluginNames)) {
55        yield return GetInformation(desc);
56      }
57    }
58
59    internal string GetInformation(string pluginName) {
60      return GetInformation(GetPluginDescription(pluginName));
61    }
62
63    private string GetInformation(PluginDescription desc) {
64      StringBuilder builder = new StringBuilder();
65      builder.Append("Name: ").AppendLine(desc.Name);
66      builder.Append("Version: ").AppendLine(desc.Version.ToString());
67      builder.AppendLine("Description:").AppendLine(desc.Description);
68      builder.Append("Build date: ").AppendLine(desc.BuildDate.ToString());
69      builder.AppendLine("Files: ");
70      foreach (string fileName in from file in desc.Files select file.Name) {
71        builder.AppendLine(fileName);
72      }
73      builder.AppendLine("Directly depends on:");
74      if (desc.Dependencies.Count() == 0) builder.AppendLine("None");
75      foreach (var dependency in desc.Dependencies) {
76        builder.AppendLine(dependency.Name);
77      }
78      builder.AppendFormat("Plugins directly dependent on {0}:\n", desc.Name);
79      var dependents = from x in pluginManager.Plugins
80                       where x.Dependencies.Contains(desc)
81                       select x.Name;
82      if (dependents.Count() == 0) builder.AppendLine("None");
83      foreach (var dependent in dependents) {
84        builder.AppendLine(dependent);
85      }
86      builder.AppendLine("This plugin is " + desc.PluginState.ToString().ToLowerInvariant() + ".");
87      if (desc.PluginState == PluginState.Disabled) {
88        builder.AppendLine(DetermineProblem(desc));
89      }
90
91      return builder.ToString();
92    }
93
94    private static string DetermineProblem(PluginDescription desc) {
95      // either any file is missing
96      StringBuilder builder = new StringBuilder();
97      var missingFiles = from x in desc.Files
98                         where !File.Exists(x.Name)
99                         select x.Name;
100      if (missingFiles.Count() > 0) {
101        foreach (string fileName in from file in desc.Files select file.Name) {
102          if (!File.Exists(fileName)) builder.Append("Missing file: ").AppendLine(fileName);
103        }
104        return builder.ToString();
105      } else {
106        // or any dependency is disabled
107        var disabledDependencies = from x in desc.Dependencies
108                                   where x.PluginState == PluginState.Disabled
109                                   select x;
110        if (disabledDependencies.Count() > 0) {
111          foreach (var dependency in disabledDependencies) {
112            builder.Append(dependency.Name).AppendLine(" is disabled.").AppendLine(DetermineProblem(dependency));
113          }
114          return builder.ToString();
115        } else {
116          // or any dependency is missing / not installed
117          var declaredDependencies = GetDeclaredDependencies(desc);
118          if (declaredDependencies.Count() != desc.Dependencies.Count()) {
119            var missingDependencies = from x in declaredDependencies
120                                      where !desc.Dependencies.Any(dep => dep.Name == x)
121                                      select x;
122            builder.AppendLine("Necessary dependencies are missing:");
123            foreach (string missingDependency in missingDependencies) {
124              builder.AppendLine(missingDependency);
125            }
126          } else {
127            // or there was a problem loading the assemblies
128            builder.AppendLine("There was a problem while loading assemblies: ");
129            foreach (AssemblyName assembly in desc.AssemblyNames) {
130              builder.AppendLine(assembly.FullName);
131            }
132            return builder.ToString();
133          }
134        }
135      }
136      return "There is an unknown problem with plugin: " + desc.Name;
137    }
138
139    private static IEnumerable<string> GetDeclaredDependencies(PluginDescription desc) {
140      var plugin = ApplicationManager.GetInstances<IPlugin>(desc).Single();
141      return plugin.GetType().GetCustomAttributes(typeof(PluginDependencyAttribute), false).Cast<PluginDependencyAttribute>().Select(x => x.Dependency);
142    }
143
144    private PluginDescription GetPluginDescription(string pluginName) {
145      var exactMatch = from pluginDesc in pluginManager.Plugins
146                       where string.Equals(pluginName, pluginDesc.Name, StringComparison.InvariantCultureIgnoreCase)
147                       select pluginDesc;
148      var inexactMatch = from pluginDesc in pluginManager.Plugins
149                         where MatchPluginNameInexact(pluginName, pluginDesc.Name)
150                         select pluginDesc;
151      return exactMatch.Count() > 0 ? exactMatch.Single() : inexactMatch.First();
152    }
153
154    private IEnumerable<PluginDescription> GetPluginDescriptions(IEnumerable<string> pluginNames) {
155      return from pluginName in pluginNames
156             select GetPluginDescription(pluginName);
157    }
158
159    private static bool MatchPluginNameInexact(string similarName, string actualName) {
160      return
161        // Core-3.2 == HeuristicLab.Core-3.2
162        actualName.Equals("HeuristicLab." + similarName, StringComparison.InvariantCultureIgnoreCase) ||
163        // HeuristicLab.Core == HeuristicLab.Core-3.2 (this should be save because we checked for exact matches first)
164        (Math.Abs(actualName.Length - similarName.Length) <= 4 && actualName.StartsWith(similarName, StringComparison.InvariantCultureIgnoreCase)) ||
165        // Core == HeuristicLab.Core-3.2
166        (Math.Abs(actualName.Length - similarName.Length) <= 17 && actualName.StartsWith("HeuristicLab." + similarName, StringComparison.InvariantCultureIgnoreCase));
167    }
168
169    public void Install(IEnumerable<string> pluginNames) {
170      throw new NotImplementedException();
171      //IEnumerable<PluginInformation> pluginsToInstall;
172      //using (UpdateLocationClient updateLocation = new UpdateLocationClient()) {
173      //  pluginsToInstall = from pluginName in pluginNames
174      //                     from matchingPlugin in updateLocation.GetAvailablePluginsByName(pluginName)
175      //                     select matchingPlugin;
176
177      //  var args = new PluginInfrastructureCancelEventArgs("Installing", pluginsToInstall);
178      //  OnPreInstall(args);
179      //  foreach (var pluginInfo in pluginsToInstall) {
180      //    var s = updateLocation.GetPluginFiles(pluginInfo);
181      //    Console.WriteLine("Downloading: {0} {1} {2}", pluginInfo.Name, pluginInfo.Version, pluginInfo.BuildDate);
182      //  }
183      //}
184      //OnInstalled(new PluginInfrastructureEventArgs("Installed", pluginsToInstall));
185    }
186
187    //private static PluginInformation GetMatchingPluginInformation(string pluginName, IEnumerable<PluginInformation> plugins) {
188    //  var exactMatch = from pluginDesc in plugins
189    //                   where string.Equals(pluginName, pluginDesc.Name, StringComparison.InvariantCultureIgnoreCase)
190    //                   select pluginDesc;
191    //  var inexactMatch = from pluginDesc in plugins
192    //                     where MatchPluginNameInexact(pluginName, pluginDesc.Name)
193    //                     select pluginDesc;
194    //  return exactMatch.Count() > 0 ? exactMatch.Single() : inexactMatch.First();
195    //}
196
197    public void Remove(IEnumerable<string> pluginNames) {
198      var fileNames = from pluginToDelete in PluginDescriptionIterator.IterateDependentsTopDown(GetPluginDescriptions(pluginNames), pluginManager.Plugins)
199                      from file in pluginToDelete.Files
200                      select Path.Combine(pluginDir, file.Name);
201      var args = new PluginInfrastructureCancelEventArgs("Deleting", fileNames);
202      OnPreDelete(args);
203      if (!args.Cancel) {
204        foreach (string fileName in fileNames) {
205          Console.WriteLine("Deleting file " + fileName);
206          // File.Delete(fileName);
207        }
208
209        OnDeleted(new PluginInfrastructureEventArgs("Deleted", fileNames));
210      }
211    }
212
213    public void Update(IEnumerable<string> pluginNames) {
214      var pluginDescriptions = from name in pluginNames
215                               select GetPluginDescription(name);
216      Dictionary<PluginInformation, string> matchingPlugins = new Dictionary<PluginInformation, string>();
217      foreach (var updateLocation in HeuristicLab.PluginInfrastructure.Properties.Settings.Default.UpdateLocations) {
218        using (UpdateLocationClient client = new UpdateLocationClient("", updateLocation)) {
219          var updateLocationMatchingPlugins = from desc in pluginDescriptions
220                                              from info in client.GetAvailablePluginsByName(desc.Name)
221                                              select info;
222          foreach (PluginInformation info in updateLocationMatchingPlugins) {
223            // keep only the highest version and most recent build of any plugin
224            var existingPlugin = matchingPlugins.Keys.FirstOrDefault(x => x.Name == info.Name);
225            if (existingPlugin == null || existingPlugin.Version < info.Version || (existingPlugin.Version == info.Version && existingPlugin.BuildDate < info.BuildDate)) {
226              matchingPlugins.Remove(existingPlugin);
227              matchingPlugins.Add(info, updateLocation);
228            }
229          }
230        }
231      }
232      PluginInfrastructureCancelEventArgs args = new PluginInfrastructureCancelEventArgs("Updating", matchingPlugins.Keys);
233      OnPreUpdate(args);
234      if (!args.Cancel) {
235        var groupedInfos = matchingPlugins.GroupBy(x => x.Value);
236        foreach (var group in groupedInfos) {
237          using (UpdateLocationClient client = new UpdateLocationClient(group.Key)) {
238            foreach (var info in group) {
239              client.GetPluginFiles(info.Key);
240            }
241          }
242        }
243        OnUpdated(new PluginInfrastructureEventArgs("Updated", matchingPlugins.Keys));
244      }
245    }
246
247    private void OnPreUpdate(PluginInfrastructureCancelEventArgs args) {
248      if (PreUpdatePlugin != null) PreUpdatePlugin(this, args);
249    }
250
251    private void OnUpdated(PluginInfrastructureEventArgs args) {
252      if (PluginUpdated != null) PluginUpdated(this, args);
253    }
254
255    private void OnPreDelete(PluginInfrastructureCancelEventArgs args) {
256      if (PreRemovePlugin != null) PreRemovePlugin(this, args);
257    }
258
259    private void OnDeleted(PluginInfrastructureEventArgs args) {
260      if (PluginRemoved != null) PluginRemoved(this, args);
261    }
262
263    private void OnPreInstall(PluginInfrastructureCancelEventArgs args) {
264      if (PreInstallPlugin != null) PreInstallPlugin(this, args);
265    }
266
267    private void OnInstalled(PluginInfrastructureEventArgs args) {
268      if (PluginInstalled != null) PluginInstalled(this, args);
269    }
270  }
271}
Note: See TracBrowser for help on using the repository browser.