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 @ 6451

Last change on this file since 6451 was 6371, checked in by ascheibe, 14 years ago

#1233

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