Free cookie consent management tool by TermsFeed Policy Generator

source: branches/HeuristicLab.Hive-3.4/sources/HeuristicLab.Clients.Hive.Slave/3.4/PluginCache.cs @ 6107

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

#1233

  • simplify PreparePlugins
  • send more exceptions to ExperimentManager
File size: 10.6 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2010 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.CompilerServices;
28using System.Runtime.Serialization;
29using System.Runtime.Serialization.Formatters.Binary;
30using System.Threading;
31
32namespace HeuristicLab.Clients.Hive.SlaveCore {
33  public class PluginCache {
34    private static object locker = new object();
35    private const string lastUsedFileName = "lastUsed.dat";
36
37    //maximum number of days after which a plugin gets deleted if not used
38    private const int maxAge = 5;
39
40    private static PluginCache instance = null;
41
42    public string PluginCacheDir { get; set; }
43    public string PluginTempBaseDir { get; set; }
44
45    private List<Guid> cachedPluginsGuids = new List<Guid>();
46
47    public static PluginCache Instance {
48      get {
49        if (instance == null)
50          instance = new PluginCache();
51        return instance;
52      }
53    }
54
55    public PluginCache() {
56      PluginCacheDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "PluginCache");
57      PluginTempBaseDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "PluginTemp");
58
59      DoUpdateRun();
60    }
61
62    /// <summary>
63    /// Returns the last directory of a path
64    /// </summary>   
65    private string GetFilenameFromPath(string path) {
66      string[] dirParts = path.Split(Path.DirectorySeparatorChar);
67      if (dirParts.Length > 0) {
68        string fileGuid = dirParts[dirParts.Length - 1];
69        return fileGuid;
70      } else
71        return "";
72    }
73
74    private void DoUpdateRun() {
75      SafelyCreateDirectory(PluginCacheDir);
76      foreach (string dir in Directory.EnumerateDirectories(PluginCacheDir)) {
77        cachedPluginsGuids.Add(Guid.Parse(GetFilenameFromPath(dir)));
78      }
79    }
80
81    [MethodImpl(MethodImplOptions.Synchronized)]
82    public void CopyPluginsForJob(List<Plugin> requests, Guid jobId, out string configFileName) {
83      lock (locker) {
84        configFileName = string.Empty;
85        String targetDir = Path.Combine(PluginTempBaseDir, jobId.ToString());
86
87        RecreateDirectory(targetDir);
88
89        foreach (Plugin requestedPlugin in requests) {
90          var filePaths = GetPluginFilePaths(requestedPlugin.Id);
91          foreach (string filePath in filePaths) {
92            File.Copy(filePath, Path.Combine(targetDir, Path.GetFileName(filePath)));
93          }
94
95          if (requestedPlugin.Name == "Configuration") {
96            configFileName = Path.Combine(targetDir, Path.GetFileName(filePaths.SingleOrDefault())); // configuration plugin consists only of 1 file (usually the "HeuristicLab X.X.exe.config")
97          }
98        }
99
100        // copy files from PluginInfrastructure (which are not declared in any plugins)
101        string baseDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
102        CopyFile(baseDir, targetDir, "HeuristicLab.PluginInfrastructure-3.3.dll");
103        CopyFile(baseDir, targetDir, "ICSharpCode.SharpZipLib.dll");
104        CopyFile(baseDir, targetDir, "ICSharpCode.SharpZipLib License.txt");
105
106        // copy slave plugins, otherwise its not possible to register the UnhandledException handler to the appdomain       
107        CopyFile(baseDir, targetDir, "HeuristicLab.Clients.Hive.SlaveCore-3.4.dll");
108        CopyFile(baseDir, targetDir, "HeuristicLab.Clients.Hive-3.4.dll");
109        //CopyFile(baseDir, targetDir, "HeuristicLab.Services.Hive.Common-3.4.dll");
110        CopyFile(baseDir, targetDir, "HeuristicLab.Hive-3.4.dll");
111        CopyFile(baseDir, targetDir, "HeuristicLab.Clients.Common-3.3.dll");
112      }
113    }
114
115    private static DirectoryInfo RecreateDirectory(String targetDir) {
116      var di = new DirectoryInfo(targetDir);
117      if (di.Exists) Directory.Delete(targetDir, true);
118      di.Refresh();
119      while (di.Exists) {
120        Thread.Sleep(50);
121        di.Refresh();
122      }
123      return SafelyCreateDirectory(targetDir);
124    }
125
126    private static DirectoryInfo SafelyCreateDirectory(String targetDir) {
127      var di = new DirectoryInfo(targetDir);
128      if (!di.Exists) {
129        di = Directory.CreateDirectory(targetDir);
130        while (!di.Exists) {
131          Thread.Sleep(50);
132          di.Refresh();
133        }
134      }
135      return di;
136    }
137
138    private void CopyFile(string baseDir, string targetDir, string fileName) {
139      if (!File.Exists(Path.Combine(targetDir, fileName))) File.Copy(Path.Combine(baseDir, fileName), Path.Combine(targetDir, fileName));
140    }
141
142    /// <summary>
143    /// Updates the plugin cache with missing plugins and
144    /// then copies the required plugins for the job.
145    /// </summary>       
146    internal void PreparePlugins(Job myJob, out string configFileName) {
147      lock (locker) {
148        SlaveClientCom.Instance.ClientCom.LogMessage("Fetching plugins for job " + myJob.Id);
149
150        List<Guid> missingGuids = new List<Guid>();
151        List<Plugin> neededPlugins = new List<Plugin>();
152        foreach (Guid pluginId in myJob.PluginsNeededIds) {
153          Plugin plugin = WcfService.Instance.GetPlugin(pluginId);
154          if (plugin != null) {
155            neededPlugins.Add(plugin);
156          }
157
158          if (!cachedPluginsGuids.Contains(pluginId)) {
159            missingGuids.Add(pluginId);
160          }
161        }
162
163        IEnumerable<PluginData> pluginDatas = WcfService.Instance.GetPluginDatas(missingGuids);
164
165        if (pluginDatas != null) {
166          foreach (PluginData pluginData in pluginDatas) {
167            string pluginDir = Path.Combine(PluginCacheDir, pluginData.PluginId.ToString());
168
169            //put all files belonging to a plugin in the same directory
170            SafelyCreateDirectory(pluginDir);
171            File.WriteAllBytes(Path.Combine(pluginDir, Path.GetFileName(pluginData.FileName)), pluginData.Data);
172          }
173
174          DoUpdateRun();
175          CopyPluginsForJob(neededPlugins, myJob.Id, out configFileName);
176        } else {
177          configFileName = "";
178        }
179
180        SlaveClientCom.Instance.ClientCom.LogMessage("Fetched " + missingGuids.Count + " plugins for job " + myJob.Id);
181      }
182    }
183
184    /// <summary>
185    /// Returns a list of files which belong to a plugin from the plugincache
186    /// </summary>
187    private IEnumerable<string> GetPluginFilePaths(Guid pluginId) {
188      string pluginPath = Path.Combine(PluginCacheDir, pluginId.ToString());
189
190      if (Directory.Exists(pluginPath)) {
191        WriteDateLastUsed(pluginPath);
192        foreach (string filePath in Directory.GetFiles(pluginPath)) {
193          string fn = Path.GetFileName(filePath);
194          if (fn != lastUsedFileName)
195            yield return filePath;
196        }
197      }
198    }
199
200    /// <summary>
201    /// creates a file in path with the current date;
202    /// this can later be used to find plugins which are outdated
203    /// </summary>   
204    private void WriteDateLastUsed(string path) {
205      FileStream fs = new FileStream(Path.Combine(path, lastUsedFileName), FileMode.Create);
206      BinaryFormatter formatter = new BinaryFormatter();
207
208      try {
209        formatter.Serialize(fs, DateTime.Now);
210      }
211      catch (SerializationException) {
212        //rethrow...
213        throw;
214      }
215      finally {
216        fs.Close();
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            SlaveClientCom.Instance.ClientCom.LogMessage("Deleting orphaned directory " + dir);
229            Directory.Delete(dir, true);
230          }
231          catch (Exception ex) {
232            SlaveClientCom.Instance.ClientCom.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        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(maxAge) < DateTime.Now) {
255              Directory.Delete(dir, true);
256              changed = true;
257            }
258          }
259          catch (SerializationException) {
260            fs.Close();
261            throw;
262          }
263          catch (System.IO.FileNotFoundException) {
264            //nerver used
265            Directory.Delete(dir, true);
266            changed = true;
267          }
268          catch (Exception) {
269            throw;
270          }
271        }
272      }
273      if (changed)
274        DoUpdateRun();
275    }
276
277
278    internal void DeletePluginsForJob(Guid id) {
279      try {
280        SlaveClientCom.Instance.ClientCom.LogMessage("unloading...");
281        int tries = 5;
282        while (tries > 0) {
283          try {
284            Directory.Delete(Path.Combine(PluginTempBaseDir, id.ToString()), true);
285            tries = 0;
286          }
287          catch (Exception e) {
288            Thread.Sleep(1000);
289            tries--;
290            if (tries == 0) throw;// TODO: don't know what do do
291          }
292        }
293      }
294      catch (Exception ex) {
295        SlaveClientCom.Instance.ClientCom.LogMessage("failed while unloading " + id + " with exception " + ex);
296      }
297      CleanPluginCache();
298    }
299  }
300}
Note: See TracBrowser for help on using the repository browser.