Free cookie consent management tool by TermsFeed Policy Generator

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

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

#1233 worked on slave and slave service installer

File size: 10.0 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    }
117
118    private void CopyFile(string baseDir, string targetDir, string fileName) {
119      if (!File.Exists(Path.Combine(targetDir, fileName))) File.Copy(Path.Combine(baseDir, fileName), Path.Combine(targetDir, fileName));
120    }
121
122    [MethodImpl(MethodImplOptions.Synchronized)]
123    internal void PreparePlugins(Job myJob, out string configFileName) {
124      lock (locker) {
125        SlaveClientCom.Instance.ClientCom.LogMessage("Fetching plugins for job");
126
127        IEnumerable<Plugin> pluginDescriptions = WcfService.Instance.GetPlugins();
128        List<Plugin> requiredPlugins = new List<Plugin>();
129        foreach (Guid pluginId in myJob.PluginsNeededIds) {
130          Plugin pl = pluginDescriptions.FirstOrDefault(p => p.Id == pluginId);
131          if (pl != null)
132            requiredPlugins.Add(pl);
133        }
134
135        List<Plugin> localPlugins = new List<Plugin>();
136        List<Plugin> missingPlugins = new List<Plugin>();
137        List<Guid> missingGuids = new List<Guid>();
138        bool found = false;
139
140        foreach (Plugin info in requiredPlugins) {
141          foreach (Guid cachedPlugin in cachedPluginsGuids) {
142            if (info.Id == cachedPlugin) {
143              localPlugins.Add(new Plugin() { Id = cachedPlugin, Name = info.Name, Version = info.Version });
144              found = true;
145
146              break;
147            }
148          }
149          if (!found) {
150            SlaveClientCom.Instance.ClientCom.LogMessage("Plugin NOT found " + info.Name + ", " + info.Version);
151            missingPlugins.Add(info);
152            missingGuids.Add(info.Id);
153          }
154          found = false;
155        }
156
157        SlaveClientCom.Instance.ClientCom.LogMessage("First run - Update the plugins in the cache");
158        localPlugins.AddRange(missingPlugins);
159
160        IEnumerable<PluginData> pluginDatas = WcfService.Instance.GetPluginDatas(missingGuids);
161
162        foreach (PluginData pluginData in pluginDatas) {
163          string pluginDir = Path.Combine(PluginCacheDir, pluginData.PluginId.ToString());
164
165          //put all files belonging to a plugin in the same directory
166          if (!Directory.Exists(pluginDir)) {
167            DirectoryInfo di = Directory.CreateDirectory(pluginDir);
168          }
169          File.WriteAllBytes(Path.Combine(pluginDir, Path.GetFileName(pluginData.FileName)), pluginData.Data);
170        }
171
172        DoUpdateRun();
173        CopyPluginsForJob(requiredPlugins, myJob.Id, out configFileName);
174      }
175    }
176
177    /// <summary>
178    /// Returns a list of files which belong to a plugin from the plugincache
179    /// </summary>
180    private IEnumerable<string> GetPluginFilePaths(Guid pluginId) {
181      string pluginPath = Path.Combine(PluginCacheDir, pluginId.ToString());
182
183      if (Directory.Exists(pluginPath)) {
184        WriteDateLastUsed(pluginPath);
185        foreach (string filePath in Directory.GetFiles(pluginPath)) {
186          string fn = Path.GetFileName(filePath);
187          if (fn != lastUsedFileName)
188            yield return filePath;
189        }
190      }
191    }
192
193    /// <summary>
194    /// creates a file in path with the current date;
195    /// this can later be used to find plugins which are outdated
196    /// </summary>   
197    private void WriteDateLastUsed(string path) {
198      FileStream fs = new FileStream(Path.Combine(path, lastUsedFileName), FileMode.Create);
199      BinaryFormatter formatter = new BinaryFormatter();
200
201      try {
202        formatter.Serialize(fs, DateTime.Now);
203      }
204      catch (SerializationException) {
205        //rethrow...
206        throw;
207      }
208      finally {
209        fs.Close();
210      }
211    }
212
213    /// <summary>
214    /// checks the pluginCacheDirectory and deletes plugin folders which are not used anymore
215    /// </summary>
216    /// <param name="path"></param>
217    private void CleanPluginCache() {
218      FileStream fs = null;
219      DateTime luDate;
220      bool changed = false;
221
222      if (Directory.Exists(PluginCacheDir)) {
223        foreach (string dir in Directory.EnumerateDirectories(PluginCacheDir)) {
224          try {
225            fs = new FileStream(Path.Combine(dir, lastUsedFileName), FileMode.Open);
226            BinaryFormatter formatter = new BinaryFormatter();
227            luDate = (DateTime)formatter.Deserialize(fs);
228            fs.Close();
229
230            if (luDate.AddDays(maxAge) < DateTime.Now) {
231              Directory.Delete(dir, true);
232              changed = true;
233            }
234          }
235          catch (SerializationException) {
236            fs.Close();
237            throw;
238          }
239          catch (System.IO.FileNotFoundException) {
240            //nerver used
241            Directory.Delete(dir, true);
242            changed = true;
243          }
244          catch (Exception) {
245            throw;
246          }
247        }
248      }
249      if (changed)
250        DoUpdateRun();
251    }
252
253
254    internal void DeletePluginsForJob(Guid id) {
255      try {
256        SlaveClientCom.Instance.ClientCom.LogMessage("unloading...");
257        int tries = 5;
258        while (tries > 0) {
259          try {
260            Directory.Delete(Path.Combine(PluginTempBaseDir, id.ToString()), true);
261            tries = 0;
262          }
263          catch (Exception e) {
264            Thread.Sleep(1000);
265            tries--;
266            if (tries == 0) throw;// TODO: don't know what do do
267          }
268        }
269      }
270      catch (Exception ex) {
271        SlaveClientCom.Instance.ClientCom.LogMessage("failed while unloading " + id + " with exception " + ex);
272      }
273      CleanPluginCache();
274    }
275  }
276}
Note: See TracBrowser for help on using the repository browser.