Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 5606 was 5599, checked in by ascheibe, 14 years ago

#1233

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