Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 5537 was 5472, checked in by ascheibe, 14 years ago

#1233

  • some gui additions
  • fix method name casing
File size: 10.2 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;
31using HeuristicLab.Services.Hive.Common.DataTransfer;
32
33namespace HeuristicLab.Clients.Hive.Slave {
34  public class PluginCache {
35    private static object locker = new object();
36    private const string lastUsedFileName = "lastUsed.dat";
37
38    //maximum number of days after which a plugin gets deleted if not used
39    private const int maxAge = 5;
40
41    private static PluginCache instance = null;
42
43    public string PluginCacheDir { get; set; }
44    public string PluginTempBaseDir { get; set; }
45
46    private List<Guid> cachedPluginsGuids = new List<Guid>();
47
48    public static PluginCache Instance {
49      get {
50        if (instance == null)
51          instance = new PluginCache();
52        return instance;
53      }
54    }
55
56    public PluginCache() {
57      PluginCacheDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "PluginCache");
58      PluginTempBaseDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "PluginTemp");
59
60      DoUpdateRun();
61    }
62
63    private string GetFilenameFromPath(string path) {
64      string[] dirParts = path.Split(Path.DirectorySeparatorChar);
65      if (dirParts.Length > 0) {
66        string fileGuid = dirParts[dirParts.Length - 1];
67        return fileGuid;
68      } else
69        return "";
70    }
71
72    private void DoUpdateRun() {
73      if (!Directory.Exists(PluginCacheDir)) {
74        Directory.CreateDirectory(PluginCacheDir);
75      }
76
77      foreach (string dir in Directory.EnumerateDirectories(PluginCacheDir)) {
78        cachedPluginsGuids.Add(Guid.Parse(GetFilenameFromPath(dir))); // Todo: cleaner solution to getFilenameFromPath
79      }
80    }
81
82    [MethodImpl(MethodImplOptions.Synchronized)]
83    public void CopyPluginsForJob(List<Plugin> requests, Guid jobId, out string configFileName) {
84      lock (locker) {
85        configFileName = string.Empty;
86        String targetDir = Path.Combine(PluginTempBaseDir, jobId.ToString());
87
88        if (Directory.Exists(targetDir)) {
89          Directory.Delete(targetDir, true);
90        }
91        Directory.CreateDirectory(targetDir);
92
93        foreach (Plugin requestedPlugin in requests) {
94          var filePaths = GetPluginFilePaths(requestedPlugin.Id);
95          foreach (string filePath in filePaths) {
96            File.Copy(filePath, Path.Combine(targetDir, Path.GetFileName(filePath)));
97          }
98
99          if (requestedPlugin.Name == "Configuration") {
100            configFileName = Path.Combine(targetDir, Path.GetFileName(filePaths.SingleOrDefault())); // configuration plugin consists only of 1 file (usually the "HeuristicLab X.X.exe.config")
101          }
102        }
103
104        // copy files from PluginInfrastructure (which are not declared in any plugins)
105        string baseDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
106        CopyFile(baseDir, targetDir, "HeuristicLab.PluginInfrastructure-3.3.dll");
107        CopyFile(baseDir, targetDir, "ICSharpCode.SharpZipLib.dll");
108        CopyFile(baseDir, targetDir, "ICSharpCode.SharpZipLib License.txt");
109
110        // copy slave plugins, otherwise its not possible to register the UnhandledException handler to the appdomain
111        CopyFile(baseDir, targetDir, "HeuristicLab.Clients.Hive.Slave-3.4.dll");
112        CopyFile(baseDir, targetDir, "HeuristicLab.Clients.Hive-3.4.dll");
113        CopyFile(baseDir, targetDir, "HeuristicLab.Services.Hive.Common-3.4.dll");
114        CopyFile(baseDir, targetDir, "HeuristicLab.Hive-3.4.dll");
115        CopyFile(baseDir, targetDir, "HeuristicLab.Clients.Common-3.3.dll");
116
117        //TODO: remove, just needed for unit tests
118        CopyFile(baseDir, targetDir, "HeuristicLab.Clients.Hive.Slave.Tests-3.4.dll");
119      }
120    }
121
122    private void CopyFile(string baseDir, string targetDir, string fileName) {
123      if (!File.Exists(Path.Combine(targetDir, fileName))) File.Copy(Path.Combine(baseDir, fileName), Path.Combine(targetDir, fileName));
124    }
125
126    [MethodImpl(MethodImplOptions.Synchronized)]
127    internal void PreparePlugins(Job myJob, out string configFileName) {
128      lock (locker) {
129        SlaveClientCom.Instance.ClientCom.LogMessage("Fetching plugins for job");
130
131        IEnumerable<Plugin> pluginDescriptions = WcfService.Instance.GetPlugins();
132        List<Plugin> requiredPlugins = new List<Plugin>();
133        foreach (Guid pluginId in myJob.PluginsNeededIds) {
134          Plugin pl = pluginDescriptions.FirstOrDefault(p => p.Id == pluginId);
135          if (pl != null)
136            requiredPlugins.Add(pl);
137        }
138
139        List<Plugin> localPlugins = new List<Plugin>();
140        List<Plugin> missingPlugins = new List<Plugin>();
141        List<Guid> missingGuids = new List<Guid>();
142        bool found = false;
143
144        foreach (Plugin info in requiredPlugins) {
145          foreach (Guid cachedPlugin in cachedPluginsGuids) {
146            if (info.Id == cachedPlugin) {
147              localPlugins.Add(new Plugin() { Id = cachedPlugin, Name = info.Name, Version = info.Version });
148              found = true;
149
150              break;
151            }
152          }
153          if (!found) {
154            SlaveClientCom.Instance.ClientCom.LogMessage("Plugin NOT found " + info.Name + ", " + info.Version);
155            missingPlugins.Add(info);
156            missingGuids.Add(info.Id);
157          }
158          found = false;
159        }
160
161        SlaveClientCom.Instance.ClientCom.LogMessage("First run - Update the plugins in the cache");
162        localPlugins.AddRange(missingPlugins);
163
164        IEnumerable<PluginData> pluginDatas = WcfService.Instance.GetPluginDatas(missingGuids);
165
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          if (!Directory.Exists(pluginDir)) {
171            DirectoryInfo di = Directory.CreateDirectory(pluginDir);
172          }
173          File.WriteAllBytes(Path.Combine(pluginDir, Path.GetFileName(pluginData.FileName)), pluginData.Data);
174        }
175
176        DoUpdateRun();
177        CopyPluginsForJob(requiredPlugins, myJob.Id, out configFileName);
178      }
179    }
180
181    /// <summary>
182    /// Returns a list of files which belong to a plugin from the plugincache
183    /// </summary>
184    private IEnumerable<string> GetPluginFilePaths(Guid pluginId) {
185      string pluginPath = Path.Combine(PluginCacheDir, pluginId.ToString());
186
187      if (Directory.Exists(pluginPath)) {
188        WriteDateLastUsed(pluginPath);
189        foreach (string filePath in Directory.GetFiles(pluginPath)) {
190          string fn = Path.GetFileName(filePath);
191          if (fn != lastUsedFileName)
192            yield return filePath;
193        }
194      }
195    }
196
197    /// <summary>
198    /// creates a file in path with the current date;
199    /// this can later be used to find plugins which are outdated
200    /// </summary>   
201    private void WriteDateLastUsed(string path) {
202      FileStream fs = new FileStream(Path.Combine(path, lastUsedFileName), FileMode.Create);
203      BinaryFormatter formatter = new BinaryFormatter();
204
205      try {
206        formatter.Serialize(fs, DateTime.Now);
207      }
208      catch (SerializationException) {
209        //rethrow...
210        throw;
211      }
212      finally {
213        fs.Close();
214      }
215    }
216
217    /// <summary>
218    /// checks the pluginCacheDirectory and deletes plugin folders which are not used anymore
219    /// </summary>
220    /// <param name="path"></param>
221    private void CleanPluginCache() {
222      FileStream fs = null;
223      DateTime luDate;
224      bool changed = false;
225
226      if (Directory.Exists(PluginCacheDir)) {
227        foreach (string dir in Directory.EnumerateDirectories(PluginCacheDir)) {
228          try {
229            fs = new FileStream(Path.Combine(dir, lastUsedFileName), FileMode.Open);
230            BinaryFormatter formatter = new BinaryFormatter();
231            luDate = (DateTime)formatter.Deserialize(fs);
232            fs.Close();
233
234            if (luDate.AddDays(maxAge) < DateTime.Now) {
235              Directory.Delete(dir, true);
236              changed = true;
237            }
238          }
239          catch (SerializationException) {
240            fs.Close();
241            throw;
242          }
243          catch (System.IO.FileNotFoundException) {
244            //nerver used
245            Directory.Delete(dir, true);
246            changed = true;
247          }
248          catch (Exception) {
249            throw;
250          }
251        }
252      }
253      if (changed)
254        DoUpdateRun();
255    }
256
257
258    internal void DeletePluginsForJob(Guid id) {
259      try {
260        SlaveClientCom.Instance.ClientCom.LogMessage("unloading...");
261        int tries = 5;
262        while (tries > 0) {
263          try {
264            Directory.Delete(Path.Combine(PluginTempBaseDir, id.ToString()), true);
265            tries = 0;
266          }
267          catch (Exception e) {
268            Thread.Sleep(1000);
269            tries--;
270            if (tries == 0) throw;// TODO: don't know what do do
271          }
272        }
273      }
274      catch (Exception ex) {
275        SlaveClientCom.Instance.ClientCom.LogMessage("failed while unloading " + id + " with exception " + ex);
276      }
277      CleanPluginCache();
278    }
279  }
280}
Note: See TracBrowser for help on using the repository browser.