Free cookie consent management tool by TermsFeed Policy Generator

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

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

Implemented changes as suggested by abeham after code review and simplified method PluginValidator.GetPluginDescription(). #863.

File size: 13.1 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
44    public IEnumerable<PluginDescription> Plugins {
45      get { return pluginManager.Plugins; }
46    }
47
48    private string pluginDir;
49    private string updateLocationUrl;
50    private PluginManager pluginManager;
51    public InstallationManager(string pluginDir) {
52      this.pluginDir = pluginDir;
53      this.updateLocationUrl = "http://localhost:59253/UpdateLocation.svc";
54      this.pluginManager = new PluginManager(pluginDir);
55      this.pluginManager.DiscoverAndCheckPlugins();
56    }
57
58    public IEnumerable<string> Show(IEnumerable<string> pluginNames) {
59      foreach (PluginDescription desc in GetPluginDescriptions(pluginNames)) {
60        yield return GetInformation(desc);
61      }
62    }
63
64    internal string GetInformation(string pluginName) {
65      return GetInformation(GetPluginDescription(pluginName));
66    }
67
68    private string GetInformation(PluginDescription desc) {
69      StringBuilder builder = new StringBuilder();
70      builder.Append("Name: ").AppendLine(desc.Name);
71      builder.Append("Version: ").AppendLine(desc.Version.ToString());
72      builder.AppendLine("Description:").AppendLine(desc.Description);
73      builder.AppendLine("This plugin is " + desc.PluginState.ToString().ToLowerInvariant() + ".");
74      builder.Append("Build date: ").AppendLine(desc.BuildDate.ToString()).AppendLine();
75      builder.AppendLine("Files: ");
76      foreach (var file in desc.Files) {
77        builder.AppendLine(file.Type + " " + file.Name);
78      }
79      builder.AppendLine().AppendLine("Directly depends on:");
80      if (desc.Dependencies.Count() == 0) builder.AppendLine("None");
81      foreach (var dependency in desc.Dependencies) {
82        builder.AppendLine(dependency.Name + " " + dependency.Version);
83      }
84      builder.AppendLine().AppendFormat("Plugins directly dependent on {0}:", desc.Name).AppendLine();
85      var dependents = from x in pluginManager.Plugins
86                       where x.Dependencies.Contains(desc)
87                       select x;
88      if (dependents.Count() == 0) builder.AppendLine("None");
89      foreach (var dependent in dependents) {
90        builder.AppendLine(dependent.Name + " " + dependent.Version);
91      }
92      builder.AppendLine();
93      if (desc.PluginState == PluginState.Disabled) {
94        builder.AppendLine(DetermineProblem(desc));
95      }
96
97      return builder.ToString();
98    }
99
100    private static string DetermineProblem(PluginDescription desc) {
101      // either any file is missing
102      StringBuilder builder = new StringBuilder();
103      builder.AppendLine("Problem report:");
104      var missingFiles = from x in desc.Files
105                         where !File.Exists(x.Name)
106                         select x.Name;
107      if (missingFiles.Count() > 0) {
108        foreach (string fileName in from file in desc.Files select file.Name) {
109          if (!File.Exists(fileName)) builder.Append("Missing file: ").AppendLine(fileName);
110        }
111        return builder.ToString();
112      } else {
113        // or any dependency is disabled
114        var disabledDependencies = from x in desc.Dependencies
115                                   where x.PluginState == PluginState.Disabled
116                                   select x;
117        if (disabledDependencies.Count() > 0) {
118          foreach (var dependency in disabledDependencies) {
119            builder.Append(dependency.Name).AppendLine(" is disabled.").AppendLine(DetermineProblem(dependency));
120          }
121          return builder.ToString();
122        } else {
123          // or any dependency is missing / not installed
124          var declaredDependencies = GetDeclaredDependencies(desc);
125          if (declaredDependencies.Count() != desc.Dependencies.Count()) {
126            var missingDependencies = from x in declaredDependencies
127                                      where !desc.Dependencies.Any(dep => dep.Name == x)
128                                      select x;
129            builder.AppendLine("Necessary dependencies are missing:");
130            foreach (string missingDependency in missingDependencies) {
131              builder.AppendLine(missingDependency);
132            }
133          } else {
134            // or there was a problem loading the assemblies
135            builder.AppendLine("There was a problem while loading assemblies: ");
136            foreach (string assemblyLocation in desc.AssemblyLocations) {
137              builder.AppendLine(assemblyLocation + ": " + AssemblyName.GetAssemblyName(assemblyLocation).FullName);
138            }
139            return builder.ToString();
140          }
141        }
142      }
143      return "There is an unknown problem with plugin: " + desc.Name;
144    }
145
146    private static IEnumerable<string> GetDeclaredDependencies(PluginDescription desc) {
147      var plugin = ApplicationManager.GetInstances<IPlugin>(desc).Single();
148      return plugin.GetType().GetCustomAttributes(typeof(PluginDependencyAttribute), false).Cast<PluginDependencyAttribute>().Select(x => x.Dependency);
149    }
150
151    private PluginDescription GetPluginDescription(string pluginName) {
152      var exactMatch = from pluginDesc in pluginManager.Plugins
153                       where string.Equals(pluginName, pluginDesc.Name, StringComparison.InvariantCultureIgnoreCase)
154                       select pluginDesc;
155      var inexactMatch = from pluginDesc in pluginManager.Plugins
156                         where MatchPluginNameInexact(pluginName, pluginDesc.Name)
157                         select pluginDesc;
158      return exactMatch.Count() > 0 ? exactMatch.Single() : inexactMatch.First();
159    }
160
161    private IEnumerable<PluginDescription> GetPluginDescriptions(IEnumerable<string> pluginNames) {
162      return from pluginName in pluginNames
163             select GetPluginDescription(pluginName);
164    }
165
166    private static bool MatchPluginNameInexact(string similarName, string actualName) {
167      return
168        // Core-3.2 == HeuristicLab.Core-3.2
169        actualName.Equals("HeuristicLab." + similarName, StringComparison.InvariantCultureIgnoreCase) ||
170        // HeuristicLab.Core == HeuristicLab.Core-3.2 (this should be save because we checked for exact matches first)
171        (Math.Abs(actualName.Length - similarName.Length) <= 4 && actualName.StartsWith(similarName, StringComparison.InvariantCultureIgnoreCase)) ||
172        // Core == HeuristicLab.Core-3.2
173        (Math.Abs(actualName.Length - similarName.Length) <= 17 && actualName.StartsWith("HeuristicLab." + similarName, StringComparison.InvariantCultureIgnoreCase));
174    }
175
176    public void Install(IEnumerable<string> pluginNames) {
177      throw new NotImplementedException();
178      //IEnumerable<PluginInformation> pluginsToInstall;
179      //using (UpdateLocationClient updateLocation = new UpdateLocationClient()) {
180      //  pluginsToInstall = from pluginName in pluginNames
181      //                     from matchingPlugin in updateLocation.GetAvailablePluginsByName(pluginName)
182      //                     select matchingPlugin;
183
184      //  var args = new PluginInfrastructureCancelEventArgs("Installing", pluginsToInstall);
185      //  OnPreInstall(args);
186      //  foreach (var pluginInfo in pluginsToInstall) {
187      //    var s = updateLocation.GetPluginFiles(pluginInfo);
188      //    Console.WriteLine("Downloading: {0} {1} {2}", pluginInfo.Name, pluginInfo.Version, pluginInfo.BuildDate);
189      //  }
190      //}
191      //OnInstalled(new PluginInfrastructureEventArgs("Installed", pluginsToInstall));
192    }
193
194    //private static PluginInformation GetMatchingPluginInformation(string pluginName, IEnumerable<PluginInformation> plugins) {
195    //  var exactMatch = from pluginDesc in plugins
196    //                   where string.Equals(pluginName, pluginDesc.Name, StringComparison.InvariantCultureIgnoreCase)
197    //                   select pluginDesc;
198    //  var inexactMatch = from pluginDesc in plugins
199    //                     where MatchPluginNameInexact(pluginName, pluginDesc.Name)
200    //                     select pluginDesc;
201    //  return exactMatch.Count() > 0 ? exactMatch.Single() : inexactMatch.First();
202    //}
203
204    public void Remove(IEnumerable<string> pluginNames) {
205      var fileNames = from pluginToDelete in PluginDescriptionIterator.IterateDependentsTopDown(GetPluginDescriptions(pluginNames), pluginManager.Plugins)
206                      from file in pluginToDelete.Files
207                      select Path.Combine(pluginDir, file.Name);
208      var args = new PluginInfrastructureCancelEventArgs("Deleting", fileNames);
209      OnPreDelete(args);
210      if (!args.Cancel) {
211        foreach (string fileName in fileNames) {
212          Console.WriteLine("Deleting file " + fileName);
213          // File.Delete(fileName);
214        }
215
216        OnDeleted(new PluginInfrastructureEventArgs("Deleted", fileNames));
217      }
218    }
219
220    public void Update(IEnumerable<string> pluginNames) {
221      var pluginDescriptions = from name in pluginNames
222                               select GetPluginDescription(name);
223      Dictionary<PluginInformation, string> matchingPlugins = new Dictionary<PluginInformation, string>();
224      foreach (var updateLocation in HeuristicLab.PluginInfrastructure.Properties.Settings.Default.UpdateLocations) {
225        using (UpdateLocationClient client = new UpdateLocationClient("", updateLocation)) {
226          var updateLocationMatchingPlugins = from desc in pluginDescriptions
227                                              from info in client.GetAvailablePluginsByName(desc.Name)
228                                              select info;
229          foreach (PluginInformation info in updateLocationMatchingPlugins) {
230            // keep only the highest version and most recent build of any plugin
231            var existingPlugin = matchingPlugins.Keys.FirstOrDefault(x => x.Name == info.Name);
232            if (existingPlugin == null || existingPlugin.Version < info.Version || (existingPlugin.Version == info.Version && existingPlugin.BuildDate < info.BuildDate)) {
233              matchingPlugins.Remove(existingPlugin);
234              matchingPlugins.Add(info, updateLocation);
235            }
236          }
237        }
238      }
239      PluginInfrastructureCancelEventArgs args = new PluginInfrastructureCancelEventArgs("Updating", matchingPlugins.Keys);
240      OnPreUpdate(args);
241      if (!args.Cancel) {
242        var groupedInfos = matchingPlugins.GroupBy(x => x.Value);
243        foreach (var group in groupedInfos) {
244          using (UpdateLocationClient client = new UpdateLocationClient(group.Key)) {
245            foreach (var info in group) {
246              client.GetPluginFiles(info.Key);
247            }
248          }
249        }
250        OnUpdated(new PluginInfrastructureEventArgs("Updated", matchingPlugins.Keys));
251      }
252    }
253
254    private void OnPreUpdate(PluginInfrastructureCancelEventArgs args) {
255      if (PreUpdatePlugin != null) PreUpdatePlugin(this, args);
256    }
257
258    private void OnUpdated(PluginInfrastructureEventArgs args) {
259      if (PluginUpdated != null) PluginUpdated(this, args);
260    }
261
262    private void OnPreDelete(PluginInfrastructureCancelEventArgs args) {
263      if (PreRemovePlugin != null) PreRemovePlugin(this, args);
264    }
265
266    private void OnDeleted(PluginInfrastructureEventArgs args) {
267      if (PluginRemoved != null) PluginRemoved(this, args);
268    }
269
270    private void OnPreInstall(PluginInfrastructureCancelEventArgs args) {
271      if (PreInstallPlugin != null) PreInstallPlugin(this, args);
272    }
273
274    private void OnInstalled(PluginInfrastructureEventArgs args) {
275      if (PluginInstalled != null) PluginInstalled(this, args);
276    }
277  }
278}
Note: See TracBrowser for help on using the repository browser.