Free cookie consent management tool by TermsFeed Policy Generator

source: branches/HeuristicLab.Hive-3.4/sources/HeuristicLab.Clients.Hive.Slave/3.4/Manager/PluginManager.cs @ 6456

Last change on this file since 6456 was 6456, checked in by ascheibe, 13 years ago

#1233

  • fixed Admin Views plugin dependencies
  • use settings instead of magic numbers and strings
File size: 11.0 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2011 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.IO;
25using System.Linq;
26using System.Reflection;
27using System.Runtime.Serialization.Formatters.Binary;
28using System.Threading;
29using HeuristicLab.Clients.Hive.SlaveCore.Properties;
30using HeuristicLab.Core;
31
32namespace HeuristicLab.Clients.Hive.SlaveCore {
33  public class PluginManager {
34    private static object locker = new object();
35    private string lastUsedFileName = Settings.Default.LastUsedFileName;
36    //maximum number of days after which a plugin gets deleted if not used
37    private int pluginLifetime = Settings.Default.PluginLifetime;
38
39    private string PluginCacheDir { get; set; }
40    public string PluginTempBaseDir { get; set; }
41    private ILog log;
42    private IPluginProvider pluginService;
43    private List<Guid> cachedPluginsGuids = new List<Guid>();
44
45    public PluginManager(IPluginProvider pluginService, ILog log) {
46      this.pluginService = pluginService;
47      this.log = log;
48      PluginCacheDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, Settings.Default.PluginCacheDir);
49      PluginTempBaseDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, Settings.Default.PluginTempBaseDir);
50      DoUpdateRun();
51    }
52
53    /// <summary>
54    /// Returns the last directory of a path
55    /// </summary>   
56    private string GetFilenameFromPath(string path) {
57      string[] dirParts = path.Split(Path.DirectorySeparatorChar);
58      if (dirParts.Length > 0) {
59        string fileGuid = dirParts[dirParts.Length - 1];
60        return fileGuid;
61      } else
62        return "";
63    }
64
65    private void DoUpdateRun() {
66      SafelyCreateDirectory(PluginCacheDir);
67      lock (cachedPluginsGuids) {
68        cachedPluginsGuids.Clear();
69        foreach (string dir in Directory.EnumerateDirectories(PluginCacheDir)) {
70          cachedPluginsGuids.Add(Guid.Parse(GetFilenameFromPath(dir)));
71        }
72      }
73    }
74
75    public void CopyPluginsForJob(List<Plugin> requests, Guid jobId, out string configFileName) {
76      configFileName = string.Empty;
77      String targetDir = Path.Combine(PluginTempBaseDir, jobId.ToString());
78
79      RecreateDirectory(targetDir);
80
81      foreach (Plugin requestedPlugin in requests) {
82        var filePaths = GetPluginFilePaths(requestedPlugin.Id);
83        foreach (string filePath in filePaths) {
84          File.Copy(filePath, Path.Combine(targetDir, Path.GetFileName(filePath)));
85        }
86
87        if (requestedPlugin.Name == Settings.Default.ConfigurationName) {
88          // configuration plugin consists only of 1 file (usually the "HeuristicLab X.X.exe.config")
89          configFileName = Path.Combine(targetDir, Path.GetFileName(filePaths.SingleOrDefault()));
90        }
91      }
92
93      // copy files from PluginInfrastructure (which are not declared in any plugins)
94      string baseDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
95      CopyFile(baseDir, targetDir, Settings.Default.PluginInfrastructureDll);
96      CopyFile(baseDir, targetDir, Settings.Default.SharpZipLibDll);
97      CopyFile(baseDir, targetDir, Settings.Default.SharpZipLibLicense);
98
99      // copy slave plugins, otherwise its not possible to register the UnhandledException handler to the appdomain       
100      CopyFile(baseDir, targetDir, Settings.Default.ClientsHiveSlaveCoreDll);
101      CopyFile(baseDir, targetDir, Settings.Default.ClientsHiveDll);
102      CopyFile(baseDir, targetDir, Settings.Default.HiveDll);
103      CopyFile(baseDir, targetDir, Settings.Default.ClientsCommonDll);
104    }
105
106    private static DirectoryInfo RecreateDirectory(String targetDir) {
107      var di = new DirectoryInfo(targetDir);
108      if (di.Exists) Directory.Delete(targetDir, true);
109      di.Refresh();
110      while (di.Exists) {
111        Thread.Sleep(Settings.Default.DirOpSleepTime);
112        di.Refresh();
113      }
114      return SafelyCreateDirectory(targetDir);
115    }
116
117    private static DirectoryInfo SafelyCreateDirectory(String targetDir) {
118      var di = new DirectoryInfo(targetDir);
119      if (!di.Exists) {
120        di = Directory.CreateDirectory(targetDir);
121        while (!di.Exists) {
122          Thread.Sleep(Settings.Default.DirOpSleepTime);
123          di.Refresh();
124        }
125      }
126      return di;
127    }
128
129    private void CopyFile(string baseDir, string targetDir, string fileName) {
130      if (!File.Exists(Path.Combine(targetDir, fileName))) File.Copy(Path.Combine(baseDir, fileName), Path.Combine(targetDir, fileName));
131    }
132
133    /// <summary>
134    /// Updates the plugin cache with missing plugins and
135    /// then copies the required plugins for the job.
136    /// </summary>       
137    public void PreparePlugins(Job job, out string configFileName) {
138      lock (locker) {
139        log.LogMessage("Fetching plugins for job " + job.Id);
140
141        List<Guid> missingGuids = new List<Guid>();
142        List<Plugin> neededPlugins = new List<Plugin>();
143        lock (cachedPluginsGuids) {
144          foreach (Guid pluginId in job.PluginsNeededIds) {
145            Plugin plugin = pluginService.GetPlugin(pluginId);
146            if (plugin != null) {
147              neededPlugins.Add(plugin);
148            }
149
150            if (!cachedPluginsGuids.Contains(pluginId)) {
151              missingGuids.Add(pluginId);
152            }
153          }
154        }
155
156        IEnumerable<PluginData> pluginDatas = pluginService.GetPluginDatas(missingGuids);
157
158        if (pluginDatas != null) {
159          foreach (PluginData pluginData in pluginDatas) {
160            string pluginDir = Path.Combine(PluginCacheDir, pluginData.PluginId.ToString());
161
162            //put all files belonging to a plugin in the same directory
163            SafelyCreateDirectory(pluginDir);
164            File.WriteAllBytes(Path.Combine(pluginDir, Path.GetFileName(pluginData.FileName)), pluginData.Data);
165          }
166
167          if (missingGuids.Count > 0) {
168            DoUpdateRun();
169          }
170          CopyPluginsForJob(neededPlugins, job.Id, out configFileName);
171        } else {
172          configFileName = "";
173        }
174        log.LogMessage(string.Format("Fetched {0} plugins for job {1}", missingGuids.Count, job.Id));
175      }
176    }
177
178    /// <summary>
179    /// Returns a list of files which belong to a plugin from the plugincache
180    /// </summary>
181    private IEnumerable<string> GetPluginFilePaths(Guid pluginId) {
182      string pluginPath = Path.Combine(PluginCacheDir, pluginId.ToString());
183
184      if (Directory.Exists(pluginPath)) {
185        WriteDateLastUsed(pluginPath);
186        foreach (string filePath in Directory.GetFiles(pluginPath)) {
187          string fn = Path.GetFileName(filePath);
188          if (fn != lastUsedFileName)
189            yield return filePath;
190        }
191      }
192    }
193
194    /// <summary>
195    /// creates a file in path with the current date;
196    /// this can later be used to find plugins which are outdated
197    /// </summary>   
198    private void WriteDateLastUsed(string path) {
199      FileStream fs = null;
200      try {
201        fs = new FileStream(Path.Combine(path, lastUsedFileName), FileMode.Create);
202        BinaryFormatter formatter = new BinaryFormatter();
203        formatter.Serialize(fs, DateTime.Now);
204      }
205      catch (IOException) {
206        log.LogMessage(string.Format("No used date written in path {0}.", path));
207      }
208      catch (SerializationException) {
209        //rethrow...
210        throw;
211      }
212      finally {
213        if (fs != null) {
214          fs.Close();
215        }
216      }
217    }
218
219    /// <summary>
220    /// Checks the PluginTemp directory for orphaned directories and deletes them.
221    /// This should be only called if no jobs are currently running.
222    /// </summary>   
223    public void CleanPluginTemp() {
224      if (Directory.Exists(PluginTempBaseDir)) {
225        foreach (string dir in Directory.EnumerateDirectories(PluginTempBaseDir)) {
226          try {
227            log.LogMessage("Deleting orphaned directory " + dir);
228            Directory.Delete(dir, true);
229          }
230          catch (Exception ex) {
231            log.LogMessage("Error cleaning up PluginTemp directory " + dir + ": " + ex.ToString());
232          }
233        }
234      }
235    }
236
237    /// <summary>
238    /// checks the pluginCacheDirectory and deletes plugin folders which are not used anymore
239    /// </summary>   
240    private void CleanPluginCache() {
241      FileStream fs = null;
242      DateTime luDate;
243      bool changed = false;
244
245      if (Directory.Exists(PluginCacheDir)) {
246        lock (locker) {
247          foreach (string dir in Directory.EnumerateDirectories(PluginCacheDir)) {
248            try {
249              fs = new FileStream(Path.Combine(dir, lastUsedFileName), FileMode.Open);
250              BinaryFormatter formatter = new BinaryFormatter();
251              luDate = (DateTime)formatter.Deserialize(fs);
252              fs.Close();
253
254              if (luDate.AddDays(pluginLifetime) < DateTime.Now) {
255                Directory.Delete(dir, true);
256                changed = true;
257              }
258            }
259            catch (FileNotFoundException) {
260              //nerver used
261              Directory.Delete(dir, true);
262              changed = true;
263            }
264            catch (Exception ex) {
265              if (fs != null) {
266                fs.Close();
267              }
268              log.LogMessage(string.Format("CleanPluginCache threw exception: {0}", ex.ToString()));
269            }
270          }
271
272          if (changed)
273            DoUpdateRun();
274        }
275      }
276    }
277
278    public void DeletePluginsForJob(Guid id) {
279      try {
280        log.LogMessage("Deleting plugins...");
281        int tries = Settings.Default.PluginDeletionRetries;
282        string path = Path.Combine(PluginTempBaseDir, id.ToString());
283        while (tries > 0) {
284          try {
285            if (Directory.Exists(path)) Directory.Delete(path, true);
286            tries = 0;
287          }
288          catch (Exception) {
289            Thread.Sleep(Settings.Default.PluginDeletionTimeout);
290            tries--;
291            if (tries == 0) throw;
292          }
293        }
294      }
295      catch (Exception ex) {
296        log.LogMessage("failed while unloading " + id + " with exception " + ex);
297      }
298      CleanPluginCache();
299    }
300  }
301}
Note: See TracBrowser for help on using the repository browser.