OpenSim
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros
PluginLoader.cs
Go to the documentation of this file.
1 /*
2  * Copyright (c) Contributors, http://opensimulator.org/
3  * See CONTRIBUTORS.TXT for a full list of copyright holders.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  * * Redistributions of source code must retain the above copyright
8  * notice, this list of conditions and the following disclaimer.
9  * * Redistributions in binary form must reproduce the above copyright
10  * notice, this list of conditions and the following disclaimer in the
11  * documentation and/or other materials provided with the distribution.
12  * * Neither the name of the OpenSimulator Project nor the
13  * names of its contributors may be used to endorse or promote products
14  * derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19  * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 using System;
29 using System.Collections.Generic;
30 using System.IO;
31 using System.Reflection;
32 using log4net;
33 using Mono.Addins;
34 
35 namespace OpenSim.Framework
36 {
40  public class PluginConstraintViolatedException : Exception
41  {
42  public PluginConstraintViolatedException () : base() {}
43  public PluginConstraintViolatedException (string msg) : base(msg) {}
44  public PluginConstraintViolatedException (string msg, Exception e) : base(msg, e) {}
45  }
46 
51  public interface IPluginConstraint
52  {
53  string Message { get; }
54  bool Apply(string extpoint);
55  }
56 
61  public interface IPluginFilter
62  {
63  bool Apply(PluginExtensionNode plugin);
64  }
65 
69  public class PluginLoader <T> : IDisposable where T : IPlugin
70  {
71  private const int max_loadable_plugins = 10000;
72 
73  private List<T> loaded = new List<T>();
74  private List<string> extpoints = new List<string>();
75  private PluginInitialiserBase initialiser;
76 
77  private Dictionary<string,IPluginConstraint> constraints
78  = new Dictionary<string,IPluginConstraint>();
79 
80  private Dictionary<string,IPluginFilter> filters
81  = new Dictionary<string,IPluginFilter>();
82 
83  private static readonly ILog log
84  = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
85 
86  public PluginInitialiserBase Initialiser
87  {
88  set { initialiser = value; }
89  get { return initialiser; }
90  }
91 
92  public List<T> Plugins
93  {
94  get { return loaded; }
95  }
96 
97  public T Plugin
98  {
99  get { return (loaded.Count == 1)? loaded [0] : default (T); }
100  }
101 
102  public PluginLoader()
103  {
104  Initialiser = new PluginInitialiserBase();
105  initialise_plugin_dir_(".");
106  }
107 
109  {
110  Initialiser = init;
111  initialise_plugin_dir_(".");
112  }
113 
114  public PluginLoader(PluginInitialiserBase init, string dir)
115  {
116  Initialiser = init;
117  initialise_plugin_dir_(dir);
118  }
119 
120  public void Add(string extpoint)
121  {
122  if (extpoints.Contains(extpoint))
123  return;
124 
125  extpoints.Add(extpoint);
126  }
127 
128  public void Add(string extpoint, IPluginConstraint cons)
129  {
130  Add(extpoint);
131  AddConstraint(extpoint, cons);
132  }
133 
134  public void Add(string extpoint, IPluginFilter filter)
135  {
136  Add(extpoint);
137  AddFilter(extpoint, filter);
138  }
139 
140  public void AddConstraint(string extpoint, IPluginConstraint cons)
141  {
142  constraints.Add(extpoint, cons);
143  }
144 
145  public void AddFilter(string extpoint, IPluginFilter filter)
146  {
147  filters.Add(extpoint, filter);
148  }
149 
150  public void Load(string extpoint)
151  {
152  Add(extpoint);
153  Load();
154  }
155 
156  public void Load()
157  {
158  foreach (string ext in extpoints)
159  {
160  log.Info("[PLUGINS]: Loading extension point " + ext);
161 
162  if (constraints.ContainsKey(ext))
163  {
164  IPluginConstraint cons = constraints[ext];
165  if (cons.Apply(ext))
166  log.Error("[PLUGINS]: " + ext + " failed constraint: " + cons.Message);
167  }
168 
169  IPluginFilter filter = null;
170 
171  if (filters.ContainsKey(ext))
172  filter = filters[ext];
173 
174  List<T> loadedPlugins = new List<T>();
175  foreach (PluginExtensionNode node in AddinManager.GetExtensionNodes(ext))
176  {
177  log.Info("[PLUGINS]: Trying plugin " + node.Path);
178 
179  if ((filter != null) && (filter.Apply(node) == false))
180  continue;
181 
182  T plugin = (T)node.CreateInstance();
183  loadedPlugins.Add(plugin);
184  }
185 
186  // We do Initialise() in a second loop after CreateInstance
187  // So that modules who need init before others can do it
188  // Example: Script Engine Component System needs to load its components before RegionLoader starts
189  foreach (T plugin in loadedPlugins)
190  {
191  Initialiser.Initialise(plugin);
192  Plugins.Add(plugin);
193  }
194  }
195  }
196 
202  public void Dispose()
203  {
204  AddinManager.AddinLoadError -= on_addinloaderror_;
205  AddinManager.AddinLoaded -= on_addinloaded_;
206  }
207 
208  private void initialise_plugin_dir_(string dir)
209  {
210  if (AddinManager.IsInitialized == true)
211  return;
212 
213  log.Info("[PLUGINS]: Initializing addin manager");
214 
215  AddinManager.AddinLoadError += on_addinloaderror_;
216  AddinManager.AddinLoaded += on_addinloaded_;
217 
218  //clear_registry_(dir);
219 
220  //suppress_console_output_(true);
221  AddinManager.Initialize(dir);
222  AddinManager.Registry.Update(null);
223  //suppress_console_output_(false);
224  }
225 
226  private void on_addinloaded_(object sender, AddinEventArgs args)
227  {
228  log.Info ("[PLUGINS]: Plugin Loaded: " + args.AddinId);
229  }
230 
231  private void on_addinloaderror_(object sender, AddinErrorEventArgs args)
232  {
233  if (args.Exception == null)
234  log.Error ("[PLUGINS]: Plugin Error: "
235  + args.Message);
236  else
237  log.Error ("[PLUGINS]: Plugin Error: "
238  + args.Exception.Message + "\n"
239  + args.Exception.StackTrace);
240  }
241 
242  private void clear_registry_(string dir)
243  {
244  // The Mono addin manager (in Mono.Addins.dll version 0.2.0.0)
245  // occasionally seems to corrupt its addin cache
246  // Hence, as a temporary solution we'll remove it before each startup
247 
248  string customDir = Environment.GetEnvironmentVariable ("MONO_ADDINS_REGISTRY");
249  string v0 = "addin-db-000";
250  string v1 = "addin-db-001";
251  if (customDir != null && customDir != String.Empty)
252  {
253  v0 = Path.Combine(customDir, v0);
254  v1 = Path.Combine(customDir, v1);
255  }
256  try
257  {
258  if (Directory.Exists(v0))
259  Directory.Delete(v0, true);
260 
261  if (Directory.Exists(v1))
262  Directory.Delete(v1, true);
263 
264  }
265  catch (IOException)
266  {
267  // If multiple services are started simultaneously, they may
268  // each test whether the directory exists at the same time, and
269  // attempt to delete the directory at the same time. However,
270  // one of the services will likely succeed first, causing the
271  // second service to throw an IOException. We catch it here and
272  // continue on our merry way.
273  // Mike 2008.08.01, patch from Zaki
274  }
275  }
276 
277  private static TextWriter prev_console_;
278  public void suppress_console_output_(bool save)
279  {
280  if (save)
281  {
282  prev_console_ = System.Console.Out;
283  System.Console.SetOut(new StreamWriter(Stream.Null));
284  }
285  else
286  {
287  if (prev_console_ != null)
288  System.Console.SetOut(prev_console_);
289  }
290  }
291  }
292 
293  public class PluginExtensionNode : ExtensionNode
294  {
295  [NodeAttribute]
296  string id = "";
297 
298  [NodeAttribute]
299  string provider = "";
300 
301  [NodeAttribute]
302  string type = "";
303 
304  Type typeobj;
305 
306  public string ID { get { return id; } }
307  public string Provider { get { return provider; } }
308  public string TypeName { get { return type; } }
309 
310  public Type TypeObject
311  {
312  get
313  {
314  if (typeobj != null)
315  return typeobj;
316 
317  if (type.Length == 0)
318  throw new InvalidOperationException("Type name not specified.");
319 
320  return typeobj = Addin.GetType(type, true);
321  }
322  }
323 
324  public object CreateInstance()
325  {
326  return Activator.CreateInstance(TypeObject);
327  }
328  }
329 
334  {
335  private int min;
336  private int max;
337 
338  public PluginCountConstraint(int exact)
339  {
340  min = exact;
341  max = exact;
342  }
343 
344  public PluginCountConstraint(int minimum, int maximum)
345  {
346  min = minimum;
347  max = maximum;
348  }
349 
350  public string Message
351  {
352  get
353  {
354  return "The number of plugins is constrained to the interval ["
355  + min + ", " + max + "]";
356  }
357  }
358 
359  public bool Apply (string extpoint)
360  {
361  int count = AddinManager.GetExtensionNodes(extpoint).Count;
362 
363  if ((count < min) || (count > max))
364  throw new PluginConstraintViolatedException(Message);
365 
366  return true;
367  }
368  }
369 
375  {
376  private string[] m_filters;
377 
384  public PluginProviderFilter(string p)
385  {
386  m_filters = p.Split(',');
387 
388  for (int i = 0; i < m_filters.Length; i++)
389  {
390  m_filters[i] = m_filters[i].Trim();
391  }
392  }
393 
399  public bool Apply (PluginExtensionNode plugin)
400  {
401  for (int i = 0; i < m_filters.Length; i++)
402  {
403  if (m_filters[i] == plugin.Provider)
404  {
405  return true;
406  }
407  }
408 
409  return false;
410  }
411  }
412 
417  {
418  private string[] m_filters;
419 
426  public PluginIdFilter(string p)
427  {
428  m_filters = p.Split(',');
429 
430  for (int i = 0; i < m_filters.Length; i++)
431  {
432  m_filters[i] = m_filters[i].Trim();
433  }
434  }
435 
441  public bool Apply (PluginExtensionNode plugin)
442  {
443  for (int i = 0; i < m_filters.Length; i++)
444  {
445  if (m_filters[i] == plugin.ID)
446  {
447  return true;
448  }
449  }
450 
451  return false;
452  }
453  }
454 }
Any plugins which need to pass parameters to their initialisers must inherit this class and use it to...
Definition: IPlugin.cs:69
PluginLoader(PluginInitialiserBase init)
void Add(string extpoint, IPluginFilter filter)
Classes wishing to impose constraints on plugin loading must implement this class and pass it to Plug...
Definition: PluginLoader.cs:51
void Add(string extpoint, IPluginConstraint cons)
PluginProviderFilter(string p)
Constructor.
Exception thrown if an incorrect number of plugins are loaded
Definition: PluginLoader.cs:40
This interface, describes a generic plugin
Definition: IPlugin.cs:45
void AddFilter(string extpoint, IPluginFilter filter)
bool Apply(PluginExtensionNode plugin)
Apply this filter to the given plugin.
PluginConstraintViolatedException(string msg, Exception e)
Definition: PluginLoader.cs:44
Filters out which plugin to load based on the plugin name or names given. Plugin names are contained ...
Classes wishing to select specific plugins from a range of possible options must implement this class...
Definition: PluginLoader.cs:61
PluginLoader(PluginInitialiserBase init, string dir)
PluginIdFilter(string p)
Constructor.
bool Apply(PluginExtensionNode plugin)
Apply this filter to plugin .
PluginCountConstraint(int minimum, int maximum)
Filters plugins according to their ID. Plugin IDs are contained in their addin.xml ...
void Dispose()
Unregisters Mono.Addins event handlers, allowing temporary Mono.Addins data to be garbage collected...
Constraint that bounds the number of plugins to be loaded.
void AddConstraint(string extpoint, IPluginConstraint cons)