OpenSim
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros
RegionModulesControllerPlugin.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.Reflection;
31 using log4net;
32 using Mono.Addins;
33 using Nini.Config;
34 using OpenSim;
35 using OpenSim.Framework;
36 using OpenSim.Region.Framework.Interfaces;
37 using OpenSim.Region.Framework.Scenes;
38 
39 namespace OpenSim.ApplicationPlugins.RegionModulesController
40 {
41  [Extension(Path = "/OpenSim/Startup", Id = "LoadRegions", NodeName = "Plugin")]
44  {
45  // Logger
46  private static readonly ILog m_log =
47  LogManager.GetLogger(
48  MethodBase.GetCurrentMethod().DeclaringType);
49 
54  public bool LoadModulesFromAddins { get; set; }
55 
56  // Config access
57  private OpenSimBase m_openSim;
58 
59  // Our name
60  private string m_name;
61 
62  // Internal lists to collect information about modules present
63  private List<TypeExtensionNode> m_nonSharedModules =
64  new List<TypeExtensionNode>();
65  private List<TypeExtensionNode> m_sharedModules =
66  new List<TypeExtensionNode>();
67 
68  // List of shared module instances, for adding to Scenes
69  private List<ISharedRegionModule> m_sharedInstances =
70  new List<ISharedRegionModule>();
71 
73  {
74  LoadModulesFromAddins = true;
75  }
76 
77 #region IApplicationPlugin implementation
78 
79  public void Initialise (OpenSimBase openSim)
80  {
81  m_openSim = openSim;
82  m_openSim.ApplicationRegistry.RegisterInterface<IRegionModulesController>(this);
83  m_log.DebugFormat("[REGIONMODULES]: Initializing...");
84 
85  if (!LoadModulesFromAddins)
86  return;
87 
88  // Who we are
89  string id = AddinManager.CurrentAddin.Id;
90 
91  // Make friendly name
92  int pos = id.LastIndexOf(".");
93  if (pos == -1)
94  m_name = id;
95  else
96  m_name = id.Substring(pos + 1);
97 
98  // The [Modules] section in the ini file
99  IConfig modulesConfig =
100  m_openSim.ConfigSource.Source.Configs["Modules"];
101  if (modulesConfig == null)
102  modulesConfig = m_openSim.ConfigSource.Source.AddConfig("Modules");
103 
104  Dictionary<RuntimeAddin, IList<int>> loadedModules = new Dictionary<RuntimeAddin, IList<int>>();
105 
106  // Scan modules and load all that aren't disabled
107  foreach (TypeExtensionNode node in AddinManager.GetExtensionNodes("/OpenSim/RegionModules"))
108  AddNode(node, modulesConfig, loadedModules);
109 
110  foreach (KeyValuePair<RuntimeAddin, IList<int>> loadedModuleData in loadedModules)
111  {
112  m_log.InfoFormat(
113  "[REGIONMODULES]: From plugin {0}, (version {1}), loaded {2} modules, {3} shared, {4} non-shared {5} unknown",
114  loadedModuleData.Key.Id,
115  loadedModuleData.Key.Version,
116  loadedModuleData.Value[0] + loadedModuleData.Value[1] + loadedModuleData.Value[2],
117  loadedModuleData.Value[0], loadedModuleData.Value[1], loadedModuleData.Value[2]);
118  }
119 
120  // Load and init the module. We try a constructor with a port
121  // if a port was given, fall back to one without if there is
122  // no port or the more specific constructor fails.
123  // This will be removed, so that any module capable of using a port
124  // must provide a constructor with a port in the future.
125  // For now, we do this so migration is easy.
126  //
127  foreach (TypeExtensionNode node in m_sharedModules)
128  {
129  Object[] ctorArgs = new Object[] { (uint)0 };
130 
131  // Read the config again
132  string moduleString =
133  modulesConfig.GetString("Setup_" + node.Id, String.Empty);
134  // Test to see if we want this module
135  if (moduleString == "disabled")
136  continue;
137 
138  // Get the port number, if there is one
139  if (moduleString != String.Empty)
140  {
141  // Get the port number from the string
142  string[] moduleParts = moduleString.Split(new char[] { '/' },
143  2);
144  if (moduleParts.Length > 1)
145  ctorArgs[0] = Convert.ToUInt32(moduleParts[0]);
146  }
147 
148  // Try loading and initilaizing the module, using the
149  // port if appropriate
150  ISharedRegionModule module = null;
151 
152  try
153  {
154  module = (ISharedRegionModule)Activator.CreateInstance(
155  node.Type, ctorArgs);
156  }
157  catch
158  {
159  module = (ISharedRegionModule)Activator.CreateInstance(
160  node.Type);
161  }
162 
163  // OK, we're up and running
164  m_sharedInstances.Add(module);
165  module.Initialise(m_openSim.ConfigSource.Source);
166  }
167  }
168 
169  public void PostInitialise ()
170  {
171  m_log.DebugFormat("[REGIONMODULES]: PostInitializing...");
172 
173  // Immediately run PostInitialise on shared modules
174  foreach (ISharedRegionModule module in m_sharedInstances)
175  {
176  module.PostInitialise();
177  }
178  }
179 
180 #endregion
181 
182 #region IPlugin implementation
183 
184  private void AddNode(
185  TypeExtensionNode node, IConfig modulesConfig, Dictionary<RuntimeAddin, IList<int>> loadedModules)
186  {
187  IList<int> loadedModuleData;
188 
189  if (!loadedModules.ContainsKey(node.Addin))
190  loadedModules.Add(node.Addin, new List<int> { 0, 0, 0 });
191 
192  loadedModuleData = loadedModules[node.Addin];
193 
194  if (node.Type.GetInterface(typeof(ISharedRegionModule).ToString()) != null)
195  {
196  if (CheckModuleEnabled(node, modulesConfig))
197  {
198  m_log.DebugFormat("[REGIONMODULES]: Found shared region module {0}, class {1}", node.Id, node.Type);
199  m_sharedModules.Add(node);
200  loadedModuleData[0]++;
201  }
202  }
203  else if (node.Type.GetInterface(typeof(INonSharedRegionModule).ToString()) != null)
204  {
205  if (CheckModuleEnabled(node, modulesConfig))
206  {
207  m_log.DebugFormat("[REGIONMODULES]: Found non-shared region module {0}, class {1}", node.Id, node.Type);
208  m_nonSharedModules.Add(node);
209  loadedModuleData[1]++;
210  }
211  }
212  else
213  {
214  m_log.WarnFormat("[REGIONMODULES]: Found unknown type of module {0}, class {1}", node.Id, node.Type);
215  loadedModuleData[2]++;
216  }
217  }
218 
219  // We don't do that here
220  //
221  public void Initialise ()
222  {
223  throw new System.NotImplementedException();
224  }
225 
226 #endregion
227 
228 #region IDisposable implementation
229 
230  // Cleanup
231  //
232  public void Dispose ()
233  {
234  // We expect that all regions have been removed already
235  while (m_sharedInstances.Count > 0)
236  {
237  m_sharedInstances[0].Close();
238  m_sharedInstances.RemoveAt(0);
239  }
240 
241  m_sharedModules.Clear();
242  m_nonSharedModules.Clear();
243  }
244 
245 #endregion
246 
247  public string Version
248  {
249  get
250  {
251  return AddinManager.CurrentAddin.Version;
252  }
253  }
254 
255  public string Name
256  {
257  get
258  {
259  return m_name;
260  }
261  }
262 
263 #region Region Module interfacesController implementation
264 
271  protected bool CheckModuleEnabled(TypeExtensionNode node, IConfig modulesConfig)
272  {
273  // Get the config string
274  string moduleString =
275  modulesConfig.GetString("Setup_" + node.Id, String.Empty);
276 
277  // We have a selector
278  if (moduleString != String.Empty)
279  {
280  // Allow disabling modules even if they don't have
281  // support for it
282  if (moduleString == "disabled")
283  return false;
284 
285  // Split off port, if present
286  string[] moduleParts = moduleString.Split(new char[] { '/' }, 2);
287  // Format is [port/][class]
288  string className = moduleParts[0];
289  if (moduleParts.Length > 1)
290  className = moduleParts[1];
291 
292  // Match the class name if given
293  if (className != String.Empty &&
294  node.Type.ToString() != className)
295  return false;
296  }
297 
298  return true;
299  }
300 
301  // The root of all evil.
302  // This is where we handle adding the modules to scenes when they
303  // load. This means that here we deal with replaceable interfaces,
304  // nonshared modules, etc.
305  //
306  public void AddRegionToModules (Scene scene)
307  {
308  Dictionary<Type, ISharedRegionModule> deferredSharedModules =
309  new Dictionary<Type, ISharedRegionModule>();
310  Dictionary<Type, INonSharedRegionModule> deferredNonSharedModules =
311  new Dictionary<Type, INonSharedRegionModule>();
312 
313  // We need this to see if a module has already been loaded and
314  // has defined a replaceable interface. It's a generic call,
315  // so this can't be used directly. It will be used later
316  Type s = scene.GetType();
317  MethodInfo mi = s.GetMethod("RequestModuleInterface");
318 
319  // This will hold the shared modules we actually load
320  List<ISharedRegionModule> sharedlist =
321  new List<ISharedRegionModule>();
322 
323  // Iterate over the shared modules that have been loaded
324  // Add them to the new Scene
325  foreach (ISharedRegionModule module in m_sharedInstances)
326  {
327  // Here is where we check if a replaceable interface
328  // is defined. If it is, the module is checked against
329  // the interfaces already defined. If the interface is
330  // defined, we simply skip the module. Else, if the module
331  // defines a replaceable interface, we add it to the deferred
332  // list.
333  Type replaceableInterface = module.ReplaceableInterface;
334  if (replaceableInterface != null)
335  {
336  MethodInfo mii = mi.MakeGenericMethod(replaceableInterface);
337 
338  if (mii.Invoke(scene, new object[0]) != null)
339  {
340  m_log.DebugFormat("[REGIONMODULE]: Not loading {0} because another module has registered {1}", module.Name, replaceableInterface.ToString());
341  continue;
342  }
343 
344  deferredSharedModules[replaceableInterface] = module;
345  m_log.DebugFormat("[REGIONMODULE]: Deferred load of {0}", module.Name);
346  continue;
347  }
348 
349  m_log.DebugFormat("[REGIONMODULE]: Adding scene {0} to shared module {1}",
350  scene.RegionInfo.RegionName, module.Name);
351 
352  module.AddRegion(scene);
353  scene.AddRegionModule(module.Name, module);
354 
355  sharedlist.Add(module);
356  }
357 
358  IConfig modulesConfig =
359  m_openSim.ConfigSource.Source.Configs["Modules"];
360 
361  // Scan for, and load, nonshared modules
362  List<INonSharedRegionModule> list = new List<INonSharedRegionModule>();
363  foreach (TypeExtensionNode node in m_nonSharedModules)
364  {
365  Object[] ctorArgs = new Object[] {0};
366 
367  // Read the config
368  string moduleString =
369  modulesConfig.GetString("Setup_" + node.Id, String.Empty);
370 
371  // We may not want to load this at all
372  if (moduleString == "disabled")
373  continue;
374 
375  // Get the port number, if there is one
376  if (moduleString != String.Empty)
377  {
378  // Get the port number from the string
379  string[] moduleParts = moduleString.Split(new char[] {'/'},
380  2);
381  if (moduleParts.Length > 1)
382  ctorArgs[0] = Convert.ToUInt32(moduleParts[0]);
383  }
384 
385  // Actually load it
386  INonSharedRegionModule module = null;
387 
388  Type[] ctorParamTypes = new Type[ctorArgs.Length];
389  for (int i = 0; i < ctorParamTypes.Length; i++)
390  ctorParamTypes[i] = ctorArgs[i].GetType();
391 
392  if (node.Type.GetConstructor(ctorParamTypes) != null)
393  module = (INonSharedRegionModule)Activator.CreateInstance(node.Type, ctorArgs);
394  else
395  module = (INonSharedRegionModule)Activator.CreateInstance(node.Type);
396 
397  // Check for replaceable interfaces
398  Type replaceableInterface = module.ReplaceableInterface;
399  if (replaceableInterface != null)
400  {
401  MethodInfo mii = mi.MakeGenericMethod(replaceableInterface);
402 
403  if (mii.Invoke(scene, new object[0]) != null)
404  {
405  m_log.DebugFormat("[REGIONMODULE]: Not loading {0} because another module has registered {1}", module.Name, replaceableInterface.ToString());
406  continue;
407  }
408 
409  deferredNonSharedModules[replaceableInterface] = module;
410  m_log.DebugFormat("[REGIONMODULE]: Deferred load of {0}", module.Name);
411  continue;
412  }
413 
414  m_log.DebugFormat("[REGIONMODULE]: Adding scene {0} to non-shared module {1}",
415  scene.RegionInfo.RegionName, module.Name);
416 
417  // Initialise the module
418  module.Initialise(m_openSim.ConfigSource.Source);
419 
420  list.Add(module);
421  }
422 
423  // Now add the modules that we found to the scene. If a module
424  // wishes to override a replaceable interface, it needs to
425  // register it in Initialise, so that the deferred module
426  // won't load.
427  foreach (INonSharedRegionModule module in list)
428  {
429  module.AddRegion(scene);
430  scene.AddRegionModule(module.Name, module);
431  }
432 
433  // Now all modules without a replaceable base interface are loaded
434  // Replaceable modules have either been skipped, or omitted.
435  // Now scan the deferred modules here
436  foreach (ISharedRegionModule module in deferredSharedModules.Values)
437  {
438  // Determine if the interface has been replaced
439  Type replaceableInterface = module.ReplaceableInterface;
440  MethodInfo mii = mi.MakeGenericMethod(replaceableInterface);
441 
442  if (mii.Invoke(scene, new object[0]) != null)
443  {
444  m_log.DebugFormat("[REGIONMODULE]: Not loading {0} because another module has registered {1}", module.Name, replaceableInterface.ToString());
445  continue;
446  }
447 
448  m_log.DebugFormat("[REGIONMODULE]: Adding scene {0} to shared module {1} (deferred)",
449  scene.RegionInfo.RegionName, module.Name);
450 
451  // Not replaced, load the module
452  module.AddRegion(scene);
453  scene.AddRegionModule(module.Name, module);
454 
455  sharedlist.Add(module);
456  }
457 
458  // Same thing for nonshared modules, load them unless overridden
459  List<INonSharedRegionModule> deferredlist =
460  new List<INonSharedRegionModule>();
461 
462  foreach (INonSharedRegionModule module in deferredNonSharedModules.Values)
463  {
464  // Check interface override
465  Type replaceableInterface = module.ReplaceableInterface;
466  if (replaceableInterface != null)
467  {
468  MethodInfo mii = mi.MakeGenericMethod(replaceableInterface);
469 
470  if (mii.Invoke(scene, new object[0]) != null)
471  {
472  m_log.DebugFormat("[REGIONMODULE]: Not loading {0} because another module has registered {1}", module.Name, replaceableInterface.ToString());
473  continue;
474  }
475  }
476 
477  m_log.DebugFormat("[REGIONMODULE]: Adding scene {0} to non-shared module {1} (deferred)",
478  scene.RegionInfo.RegionName, module.Name);
479 
480  module.Initialise(m_openSim.ConfigSource.Source);
481 
482  list.Add(module);
483  deferredlist.Add(module);
484  }
485 
486  // Finally, load valid deferred modules
487  foreach (INonSharedRegionModule module in deferredlist)
488  {
489  module.AddRegion(scene);
490  scene.AddRegionModule(module.Name, module);
491  }
492 
493  // This is needed for all module types. Modules will register
494  // Interfaces with scene in AddScene, and will also need a means
495  // to access interfaces registered by other modules. Without
496  // this extra method, a module attempting to use another modules's
497  // interface would be successful only depending on load order,
498  // which can't be depended upon, or modules would need to resort
499  // to ugly kludges to attempt to request interfaces when needed
500  // and unneccessary caching logic repeated in all modules.
501  // The extra function stub is just that much cleaner
502  //
503  foreach (ISharedRegionModule module in sharedlist)
504  {
505  module.RegionLoaded(scene);
506  }
507 
508  foreach (INonSharedRegionModule module in list)
509  {
510  module.RegionLoaded(scene);
511  }
512 
513  scene.AllModulesLoaded();
514  }
515 
516  public void RemoveRegionFromModules (Scene scene)
517  {
518  foreach (IRegionModuleBase module in scene.RegionModules.Values)
519  {
520  m_log.DebugFormat("[REGIONMODULE]: Removing scene {0} from module {1}",
521  scene.RegionInfo.RegionName, module.Name);
522  module.RemoveRegion(scene);
523  if (module is INonSharedRegionModule)
524  {
525  // as we were the only user, this instance has to die
526  module.Close();
527  }
528  }
529  scene.RegionModules.Clear();
530  }
531 
532 #endregion
533 
534  }
535 }
OpenSimulator Application Plugin framework interface
bool CheckModuleEnabled(TypeExtensionNode node, IConfig modulesConfig)
Check that the given module is no disabled in the [Modules] section of the config files...
Interactive OpenSim region server
Definition: OpenSim.cs:55
Common OpenSimulator simulator code
Definition: OpenSimBase.cs:58