- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/sources/HeuristicLab.PluginInfrastructure/Loader.cs
r11 r29 33 33 public delegate void PluginLoadFailedEventHandler(string pluginName, string args); 34 34 35 private Dictionary<PluginInfo, IPlugin> activePlugins = new Dictionary<PluginInfo, IPlugin>(); 35 private Dictionary<PluginInfo, List<string>> pluginDependencies = new Dictionary<PluginInfo, List<string>>(); 36 private List<PluginInfo> preloadedPluginInfos = new List<PluginInfo>(); 37 private Dictionary<IPlugin, PluginInfo> pluginInfos = new Dictionary<IPlugin, PluginInfo>(); 36 38 private Dictionary<PluginInfo, IPlugin> allPlugins = new Dictionary<PluginInfo, IPlugin>(); 37 private Dictionary<IPlugin, PluginInfo> pluginInfos = new Dictionary<IPlugin, PluginInfo>(); 38 39 private Dictionary<string, List<string>> pluginDependencies = new Dictionary<string, List<string>>(); 40 private Dictionary<string, List<string>> pluginAssemblies = new Dictionary<string, List<string>>(); 41 42 private List<string> loadablePlugins = new List<string>(); 39 private List<PluginInfo> disabledPlugins = new List<PluginInfo>(); 43 40 private string pluginDir = Application.StartupPath + "/" + HeuristicLab.PluginInfrastructure.Properties.Settings.Default.PluginDir; 44 41 45 42 internal event PluginLoadFailedEventHandler MissingPluginFile; 46 47 43 internal event PluginManagerActionEventHandler PluginAction; 48 44 49 internal PluginInfo[]ActivePlugins {45 internal ICollection<PluginInfo> ActivePlugins { 50 46 get { 51 PluginInfo[] plugins = new PluginInfo[activePlugins.Count]; 52 activePlugins.Keys.CopyTo(plugins, 0); 53 return plugins; 54 } 55 } 56 57 internal List<PluginInfo> InstalledPlugins { 47 List<PluginInfo> list = new List<PluginInfo>(); 48 foreach(PluginInfo info in allPlugins.Keys) { 49 if(!disabledPlugins.Exists(delegate(PluginInfo disabledInfo) { return info.Name == disabledInfo.Name; })) { 50 list.Add(info); 51 } 52 } 53 return list; 54 } 55 } 56 57 internal ICollection<PluginInfo> InstalledPlugins { 58 58 get { 59 59 return new List<PluginInfo>(allPlugins.Keys); … … 61 61 } 62 62 63 private ApplicationInfo[] applications; 64 internal ApplicationInfo[] InstalledApplications { 63 internal ICollection<PluginInfo> DisabledPlugins { 64 get { 65 return disabledPlugins; 66 } 67 } 68 69 private ICollection<ApplicationInfo> applications; 70 internal ICollection<ApplicationInfo> InstalledApplications { 65 71 get { 66 72 return applications; … … 69 75 70 76 private IPlugin FindPlugin(PluginInfo plugin) { 71 return a ctivePlugins[plugin];77 return allPlugins[plugin]; 72 78 } 73 79 … … 85 91 internal void Init() { 86 92 AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += delegate(object sender, ResolveEventArgs args) { return Assembly.ReflectionOnlyLoad(args.Name); }; 87 activePlugins.Clear();88 93 allPlugins.Clear(); 94 disabledPlugins.Clear(); 89 95 pluginInfos.Clear(); 90 96 pluginsByName.Clear(); 91 loadablePlugins.Clear();92 97 pluginDependencies.Clear(); 93 pluginAssemblies.Clear();94 98 95 99 List<Assembly> assemblies = ReflectionOnlyLoadDlls(); 96 100 CheckAssemblyDependencies(assemblies); 101 CheckPluginFiles(); 102 CheckPluginDependencies(); 97 103 LoadPlugins(); 98 CheckPluginFiles();99 104 100 105 DiscoveryService service = new DiscoveryService(); 101 106 IApplication[] apps = service.GetInstances<IApplication>(); 102 applications = new ApplicationInfo[apps.Length]; 103 104 int i = 0; 107 applications = new List<ApplicationInfo>(); 108 105 109 foreach(IApplication application in apps) { 106 110 ApplicationInfo info = new ApplicationInfo(); … … 111 115 info.PluginType = application.GetType().Namespace + "." + application.GetType().Name; 112 116 113 applications [i++] = info;117 applications.Add(info); 114 118 } 115 119 } … … 129 133 130 134 private void CheckAssemblyDependencies(List<Assembly> assemblies) { 131 132 135 foreach(Assembly assembly in assemblies) { 133 134 136 // GetExportedTypes throws FileNotFoundException when a referenced assembly 135 137 // of the current assembly is missing. … … 138 140 139 141 foreach(Type t in exported) { 140 // if the typeimplements IPlugin142 // if there is a type that implements IPlugin 141 143 if(Array.Exists<Type>(t.GetInterfaces(), delegate(Type iface) { 142 144 // use AssemblyQualifiedName to compare the types because we can't directly … … 144 146 return iface.AssemblyQualifiedName == typeof(IPlugin).AssemblyQualifiedName; 145 147 })) { 148 // fetch the attributes of the IPlugin type 146 149 GetPluginAttributeData(t); 147 150 } 148 149 151 } 150 152 } catch(FileNotFoundException) { 151 153 // when a referenced assembly cannot be loaded then ignore this assembly in the plugin discovery 152 // TASK: add the assembly to some kind of unloadable assemblies list 153 // this list could be displayed to the user for diagnosis 154 } 155 } 156 157 foreach(string pluginName in this.pluginDependencies.Keys) { 158 CheckPluginDependencies(pluginName); 154 } 159 155 } 160 156 } … … 169 165 // get all attributes of that type 170 166 IList<CustomAttributeData> attributes = CustomAttributeData.GetCustomAttributes(t); 171 172 167 List<string> pluginAssemblies = new List<string>(); 173 168 List<string> pluginDependencies = new List<string>(); 169 List<string> pluginFiles = new List<string>(); 174 170 string pluginName = ""; 175 176 // extract relevant parameters177 171 // iterate through all custom attributes and search for named arguments that we are interested in 178 172 foreach(CustomAttributeData attributeData in attributes) { 179 173 List<CustomAttributeNamedArgument> namedArguments = new List<CustomAttributeNamedArgument>(attributeData.NamedArguments); 180 181 174 // if the current attribute contains a named argument with the name "Name" then extract the plugin name 182 175 CustomAttributeNamedArgument pluginNameArgument = namedArguments.Find(delegate(CustomAttributeNamedArgument arg) { … … 186 179 pluginName = (string)pluginNameArgument.TypedValue.Value; 187 180 } 188 189 181 // if the current attribute contains a named argument with the name "Dependency" then extract the dependency 190 182 // and store it in the list of all dependencies … … 192 184 return arg.MemberInfo.Name == "Dependency"; 193 185 }); 194 195 186 if(dependencyNameArgument.MemberInfo != null) { 196 187 pluginDependencies.Add((string)dependencyNameArgument.TypedValue.Value); 197 188 } 198 199 189 // if the current attribute has a named argument "Filename" then find if the argument "Filetype" is also supplied 200 190 // and if the filetype is Assembly then store the name of the assembly in the list of assemblies … … 206 196 }); 207 197 if(filenameArg.MemberInfo != null && filetypeArg.MemberInfo != null) { 198 pluginFiles.Add(pluginDir + "/" + (string)filenameArg.TypedValue.Value); 208 199 if((PluginFileType)filetypeArg.TypedValue.Value == PluginFileType.Assembly) { 209 200 pluginAssemblies.Add(pluginDir + "/" + (string)filenameArg.TypedValue.Value); … … 212 203 } 213 204 214 // m ake sure that we found reasonable values205 // minimal sanity check of the attribute values 215 206 if(pluginName != "" && pluginAssemblies.Count > 0) { 216 this.pluginDependencies[pluginName] = pluginDependencies; 217 this.pluginAssemblies[pluginName] = pluginAssemblies; 207 // create a temporary PluginInfo that contains the attribute values 208 PluginInfo info = new PluginInfo(); 209 info.Name = pluginName; 210 info.Assemblies = pluginAssemblies; 211 info.Files.AddRange(pluginFiles); 212 info.Assemblies.AddRange(pluginAssemblies); 213 this.pluginDependencies[info] = pluginDependencies; 214 preloadedPluginInfos.Add(info); 218 215 } else { 219 216 throw new InvalidPluginException(); … … 221 218 } 222 219 220 private void CheckPluginDependencies() { 221 foreach(PluginInfo pluginInfo in preloadedPluginInfos) { 222 // don't need to check plugins that are already disabled 223 if(disabledPlugins.Contains(pluginInfo)) { 224 continue; 225 } 226 visitedDependencies.Clear(); 227 if(!CheckPluginDependencies(pluginInfo.Name)) { 228 disabledPlugins.Add(pluginInfo); 229 } 230 } 231 } 232 233 private List<string> visitedDependencies = new List<string>(); 223 234 private bool CheckPluginDependencies(string pluginName) { 224 // when we already checked the dependencies of this plugin earlier then just return true 225 if(loadablePlugins.Contains(pluginName)) { 226 return true; 227 } else if(!pluginAssemblies.ContainsKey(pluginName)) { 235 if(!preloadedPluginInfos.Exists(delegate(PluginInfo info) { return pluginName == info.Name; }) || 236 disabledPlugins.Exists(delegate(PluginInfo info) { return pluginName == info.Name; }) || 237 visitedDependencies.Contains(pluginName)) { 228 238 // when the plugin is not available return false; 229 239 return false; … … 231 241 // otherwise check if all dependencies of the plugin are OK 232 242 // if yes then this plugin is also ok and we store it in the list of loadable plugins 233 foreach(string dependency in pluginDependencies[pluginName]) { 243 244 PluginInfo matchingInfo = preloadedPluginInfos.Find(delegate(PluginInfo info) { return info.Name == pluginName; }); 245 if(matchingInfo == null) throw new InvalidProgramException(); // shouldn't happen 246 foreach(string dependency in pluginDependencies[matchingInfo]) { 247 visitedDependencies.Add(pluginName); 234 248 if(CheckPluginDependencies(dependency) == false) { 235 249 // if only one dependency is not available that means that the current plugin also is unloadable 236 250 return false; 237 251 } 238 }239 // all dependencies OK -> add to loadable list and return true240 loadablePlugins.Add(pluginName);252 visitedDependencies.Remove(pluginName); 253 } 254 // all dependencies OK 241 255 return true; 242 256 } … … 245 259 246 260 private Dictionary<string, IPlugin> pluginsByName = new Dictionary<string, IPlugin>(); 247 248 261 private void LoadPlugins() { 249 262 // load all loadable plugins (all dependencies available) into the execution context 250 foreach( string plugin in loadablePlugins) {251 {252 foreach(string assembly in plugin Assemblies[plugin]) {263 foreach(PluginInfo pluginInfo in preloadedPluginInfos) { 264 if(!disabledPlugins.Contains(pluginInfo)) { 265 foreach(string assembly in pluginInfo.Assemblies) { 253 266 Assembly.LoadFrom(assembly); 254 267 } … … 263 276 continue; 264 277 Type[] availablePluginTypes = service.GetTypes(typeof(IPlugin), assembly); 265 266 267 278 foreach(Type pluginType in availablePluginTypes) { 268 279 if(!pluginType.IsAbstract && !pluginType.IsInterface && !pluginType.HasElementType) { 269 280 IPlugin plugin = (IPlugin)Activator.CreateInstance(pluginType); 270 281 PluginAction(this, new PluginManagerActionEventArgs(plugin.Name, PluginManagerAction.InitializingPlugin)); 271 272 282 pluginsByName.Add(plugin.Name, plugin); 273 PluginInfo pluginInfo = GetPluginInfo(plugin); 274 275 276 allPlugins.Add(pluginInfo, plugin); 277 PluginAction(this, new PluginManagerActionEventArgs(plugin.Name, PluginManagerAction.InitializedPlugin)); 278 } 279 } 280 } 281 } 282 283 283 } 284 } 285 } 286 287 foreach(IPlugin plugin in pluginsByName.Values) { 288 PluginInfo pluginInfo = GetPluginInfo(plugin); 289 allPlugins.Add(pluginInfo, plugin); 290 PluginAction(this, new PluginManagerActionEventArgs(plugin.Name, PluginManagerAction.InitializedPlugin)); 291 } 292 } 284 293 private PluginInfo GetPluginInfo(IPlugin plugin) { 285 294 if(pluginInfos.ContainsKey(plugin)) { 286 295 return pluginInfos[plugin]; 287 296 } 288 289 297 // store the data of the plugin in a description file which can be used without loading the plugin assemblies 290 298 PluginInfo pluginInfo = new PluginInfo(); … … 299 307 }); 300 308 301 // each plugin can have multiple assemlies associated 302 // for each assembly of the plugin find the dependencies 303 // and get the pluginDescriptions for all dependencies 304 foreach(string assembly in pluginAssemblies[plugin.Name]) { 309 PluginInfo preloadedInfo = preloadedPluginInfos.Find(delegate(PluginInfo info) { return info.Name == plugin.Name; }); 310 foreach(string assembly in preloadedInfo.Assemblies) { 305 311 // always use \ as directory separator (this is necessary for discovery of types in 306 312 // plugins see DiscoveryService.GetTypes() 307 313 pluginInfo.Assemblies.Add(assembly.Replace('/', '\\')); 308 309 } 310 foreach(string dependency in pluginDependencies[plugin.Name]) { 314 } 315 foreach(string dependency in pluginDependencies[preloadedInfo]) { 311 316 // accumulate the dependencies of each assembly into the dependencies of the whole plugin 312 317 PluginInfo dependencyInfo = GetPluginInfo(pluginsByName[dependency]); 313 318 pluginInfo.Dependencies.Add(dependencyInfo); 314 319 } 315 316 320 pluginInfos[plugin] = pluginInfo; 317 318 321 return pluginInfo; 319 322 } 320 323 321 324 private void CheckPluginFiles() { 322 foreach(PluginInfo plugin in allPlugins.Keys) { 323 CheckPluginFiles(plugin); 325 foreach(PluginInfo plugin in preloadedPluginInfos) { 326 if(!CheckPluginFiles(plugin)) { 327 disabledPlugins.Add(plugin); 328 } 324 329 } 325 330 } 326 331 327 332 private bool CheckPluginFiles(PluginInfo pluginInfo) { 328 if(activePlugins.ContainsKey(pluginInfo)) {329 return true;330 }331 foreach(PluginInfo dependency in pluginInfo.Dependencies) {332 if(!CheckPluginFiles(dependency)) {333 return false;334 }335 }336 333 foreach(string filename in pluginInfo.Files) { 337 334 if(!File.Exists(filename)) { 338 MissingPluginFile(pluginInfo.Name, filename); 335 if(MissingPluginFile != null) { 336 MissingPluginFile(pluginInfo.Name, filename); 337 } 339 338 return false; 340 339 } 341 340 } 342 343 activePlugins.Add(pluginInfo, allPlugins[pluginInfo]);344 341 return true; 345 342 }
Note: See TracChangeset
for help on using the changeset viewer.